001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.mappaint.styleelement;
003
004import java.awt.Color;
005import java.util.Objects;
006
007import org.openstreetmap.josm.Main;
008import org.openstreetmap.josm.data.osm.OsmPrimitive;
009import org.openstreetmap.josm.data.osm.Relation;
010import org.openstreetmap.josm.data.osm.Way;
011import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
012import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
013import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
014import org.openstreetmap.josm.gui.mappaint.Cascade;
015import org.openstreetmap.josm.gui.mappaint.Environment;
016import org.openstreetmap.josm.gui.mappaint.Keyword;
017import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
018import org.openstreetmap.josm.tools.CheckParameterUtil;
019import org.openstreetmap.josm.tools.Utils;
020
021/**
022 * This is the style that defines how an area is filled.
023 */
024public class AreaElement extends StyleElement {
025
026    /**
027     * If fillImage == null, color is the fill-color, otherwise
028     * an arbitrary color value sampled from the fillImage
029     */
030    public Color color;
031
032    /**
033     * An image to cover this area. May be null to disable this feature.
034     */
035    public MapImage fillImage;
036
037    /**
038     * The text that should be written on this area.
039     */
040    public TextLabel text;
041
042    /**
043     * Fill the area only partially from the borders
044     * <p>
045     * Public access is discouraged.
046     * @see StyledMapRenderer#drawArea(Way, Color, MapImage, Float, Float, boolean, TextLabel)
047     */
048    public Float extent;
049
050    /**
051     * Areas smaller than this are filled no matter what value {@link #extent} has.
052     * <p>
053     * Public access is discouraged.
054     * @see StyledMapRenderer#drawArea(Way, Color, MapImage, Float, Float, boolean, TextLabel)
055     */
056    public Float extentThreshold;
057
058    protected AreaElement(Cascade c, Color color, MapImage fillImage, Float extent, Float extentThreshold, TextLabel text) {
059        super(c, 1f);
060        CheckParameterUtil.ensureParameterNotNull(color);
061        this.color = color;
062        this.fillImage = fillImage;
063        this.extent = extent;
064        this.extentThreshold = extentThreshold;
065        this.text = text;
066    }
067
068    /**
069     * Create a new {@link AreaElement}
070     * @param env The current style definitions
071     * @return The area element or <code>null</code> if the area should not be filled.
072     */
073    public static AreaElement create(final Environment env) {
074        final Cascade c = env.mc.getCascade(env.layer);
075        MapImage fillImage = null;
076        Color color;
077
078        IconReference iconRef = c.get(FILL_IMAGE, null, IconReference.class);
079        if (iconRef != null) {
080            fillImage = new MapImage(iconRef.iconName, iconRef.source, false);
081
082            color = new Color(fillImage.getImage(false).getRGB(
083                    fillImage.getWidth() / 2, fillImage.getHeight() / 2)
084            );
085
086            fillImage.alpha = Math.min(255, Math.max(0, Main.pref.getInteger("mappaint.fill-image-alpha", 255)));
087            Integer pAlpha = Utils.colorFloat2int(c.get(FILL_OPACITY, null, float.class));
088            if (pAlpha != null) {
089                fillImage.alpha = pAlpha;
090            }
091        } else {
092            color = c.get(FILL_COLOR, null, Color.class);
093            if (color != null) {
094                int alpha = color.getAlpha();
095                if (alpha == 255) {
096                    // Assume alpha value has not been specified by the user if
097                    // is set to fully opaque. Use default value in this case.
098                    // It is not an ideal solution, but a little tricky to get this
099                    // right, especially as named map colors can be changed in
100                    // the preference GUI and written to the preferences file.
101                    alpha = Math.min(255, Math.max(0, Main.pref.getInteger("mappaint.fillalpha", 50)));
102                }
103                Integer pAlpha = Utils.colorFloat2int(c.get(FILL_OPACITY, null, float.class));
104                if (pAlpha != null) {
105                    alpha = pAlpha;
106                }
107                color = new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);
108            }
109        }
110
111        TextLabel text = null;
112        Keyword textPos = c.get(TEXT_POSITION, null, Keyword.class);
113        if (textPos == null || "center".equals(textPos.val)) {
114            text = TextLabel.create(env, PaintColors.AREA_TEXT.get(), true);
115        }
116
117        Float extent = c.get(FILL_EXTENT, null, float.class);
118        Float extentThreshold = c.get(FILL_EXTENT_THRESHOLD, null, float.class);
119
120        if (color != null)
121            return new AreaElement(c, color, fillImage, extent, extentThreshold, text);
122        else
123            return null;
124    }
125
126    @Override
127    public void paintPrimitive(OsmPrimitive osm, MapPaintSettings paintSettings, StyledMapRenderer painter,
128            boolean selected, boolean outermember, boolean member) {
129        Color myColor = color;
130        if (osm instanceof Way) {
131            if (color != null) {
132                if (selected) {
133                    myColor = paintSettings.getSelectedColor(color.getAlpha());
134                } else if (outermember) {
135                    myColor = paintSettings.getRelationSelectedColor(color.getAlpha());
136                }
137            }
138            painter.drawArea((Way) osm, myColor, fillImage, extent, extentThreshold, painter.isInactiveMode() || osm.isDisabled(), text);
139        } else if (osm instanceof Relation) {
140            if (color != null && (selected || outermember)) {
141                myColor = paintSettings.getRelationSelectedColor(color.getAlpha());
142            }
143            painter.drawArea((Relation) osm, myColor, fillImage, extent, extentThreshold, painter.isInactiveMode() || osm.isDisabled(), text);
144        }
145    }
146
147    @Override
148    public boolean equals(Object obj) {
149        if (this == obj) return true;
150        if (obj == null || getClass() != obj.getClass()) return false;
151        if (!super.equals(obj)) return false;
152        AreaElement that = (AreaElement) obj;
153        return Objects.equals(color, that.color) &&
154                Objects.equals(fillImage, that.fillImage) &&
155                Objects.equals(text, that.text) &&
156                Objects.equals(extent, that.extent) &&
157                Objects.equals(extentThreshold, that.extentThreshold);
158    }
159
160    @Override
161    public int hashCode() {
162        return Objects.hash(super.hashCode(), color, fillImage, text, extent, extentThreshold);
163    }
164
165    @Override
166    public String toString() {
167        return "AreaElemStyle{" + super.toString() + "color=" + Utils.toString(color) +
168                " fillImage=[" + fillImage + "]}";
169    }
170}