[3880] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
[9278] | 2 | package org.openstreetmap.josm.gui.mappaint.styleelement;
|
---|
[3880] | 3 |
|
---|
| 4 | import java.awt.Color;
|
---|
| 5 | import java.awt.Font;
|
---|
[7083] | 6 | import java.util.Objects;
|
---|
[3987] | 7 |
|
---|
[3899] | 8 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
---|
[9278] | 9 | import org.openstreetmap.josm.gui.mappaint.Cascade;
|
---|
| 10 | import org.openstreetmap.josm.gui.mappaint.Environment;
|
---|
| 11 | import org.openstreetmap.josm.gui.mappaint.Keyword;
|
---|
[4007] | 12 | import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.TagKeyReference;
|
---|
[9278] | 13 | import org.openstreetmap.josm.gui.mappaint.StyleKeys;
|
---|
| 14 | import org.openstreetmap.josm.gui.mappaint.styleelement.LabelCompositionStrategy.DeriveLabelFromNameTagsCompositionStrategy;
|
---|
| 15 | import org.openstreetmap.josm.gui.mappaint.styleelement.LabelCompositionStrategy.StaticLabelCompositionStrategy;
|
---|
| 16 | import org.openstreetmap.josm.gui.mappaint.styleelement.LabelCompositionStrategy.TagLookupCompositionStrategy;
|
---|
[3880] | 17 | import org.openstreetmap.josm.tools.CheckParameterUtil;
|
---|
[3979] | 18 | import org.openstreetmap.josm.tools.Utils;
|
---|
[3880] | 19 |
|
---|
[3987] | 20 | /**
|
---|
| 21 | * Represents the rendering style for a textual label placed somewhere on the map.
|
---|
[7402] | 22 | * @since 3880
|
---|
[3987] | 23 | */
|
---|
[9278] | 24 | public class TextLabel implements StyleKeys {
|
---|
[6889] | 25 | public static final LabelCompositionStrategy AUTO_LABEL_COMPOSITION_STRATEGY = new DeriveLabelFromNameTagsCompositionStrategy();
|
---|
[3987] | 26 |
|
---|
| 27 | /** the strategy for building the actual label value for a given a {@link OsmPrimitive}.
|
---|
| 28 | * Check for null before accessing.
|
---|
| 29 | */
|
---|
| 30 | public LabelCompositionStrategy labelCompositionStrategy;
|
---|
| 31 | /** the font to be used when rendering*/
|
---|
[3880] | 32 | public Font font;
|
---|
| 33 | public int xOffset;
|
---|
| 34 | public int yOffset;
|
---|
| 35 | public Color color;
|
---|
[3979] | 36 | public Float haloRadius;
|
---|
| 37 | public Color haloColor;
|
---|
[3880] | 38 |
|
---|
[3987] | 39 | /**
|
---|
| 40 | * Creates a new text element
|
---|
[6070] | 41 | *
|
---|
[3987] | 42 | * @param strategy the strategy indicating how the text is composed for a specific {@link OsmPrimitive} to be rendered.
|
---|
| 43 | * If null, no label is rendered.
|
---|
| 44 | * @param font the font to be used. Must not be null.
|
---|
[8470] | 45 | * @param xOffset x offset
|
---|
| 46 | * @param yOffset y offset
|
---|
[3987] | 47 | * @param color the color to be used. Must not be null
|
---|
[8470] | 48 | * @param haloRadius halo radius
|
---|
| 49 | * @param haloColor halo color
|
---|
[3987] | 50 | */
|
---|
[9278] | 51 | public TextLabel(LabelCompositionStrategy strategy, Font font, int xOffset, int yOffset, Color color, Float haloRadius, Color haloColor) {
|
---|
[3880] | 52 | CheckParameterUtil.ensureParameterNotNull(font);
|
---|
| 53 | CheckParameterUtil.ensureParameterNotNull(color);
|
---|
[3987] | 54 | labelCompositionStrategy = strategy;
|
---|
[3880] | 55 | this.font = font;
|
---|
| 56 | this.xOffset = xOffset;
|
---|
| 57 | this.yOffset = yOffset;
|
---|
| 58 | this.color = color;
|
---|
[3979] | 59 | this.haloRadius = haloRadius;
|
---|
| 60 | this.haloColor = haloColor;
|
---|
[3880] | 61 | }
|
---|
| 62 |
|
---|
[3987] | 63 | /**
|
---|
| 64 | * Copy constructor
|
---|
[6070] | 65 | *
|
---|
[3987] | 66 | * @param other the other element.
|
---|
| 67 | */
|
---|
[9278] | 68 | public TextLabel(TextLabel other) {
|
---|
[3987] | 69 | this.labelCompositionStrategy = other.labelCompositionStrategy;
|
---|
[3979] | 70 | this.font = other.font;
|
---|
| 71 | this.xOffset = other.xOffset;
|
---|
| 72 | this.yOffset = other.yOffset;
|
---|
| 73 | this.color = other.color;
|
---|
| 74 | this.haloColor = other.haloColor;
|
---|
| 75 | this.haloRadius = other.haloRadius;
|
---|
| 76 | }
|
---|
| 77 |
|
---|
[3987] | 78 | /**
|
---|
[7402] | 79 | * Derives a suitable label composition strategy from the style properties in {@code c}.
|
---|
[6070] | 80 | *
|
---|
[3987] | 81 | * @param c the style properties
|
---|
[9239] | 82 | * @param defaultAnnotate whether to return {@link #AUTO_LABEL_COMPOSITION_STRATEGY} if not strategy is found
|
---|
| 83 | * @return the label composition strategy, or {@code null}
|
---|
[3987] | 84 | */
|
---|
[8510] | 85 | protected static LabelCompositionStrategy buildLabelCompositionStrategy(Cascade c, boolean defaultAnnotate) {
|
---|
[4007] | 86 | /*
|
---|
| 87 | * If the cascade includes a TagKeyReference we will lookup the rendered label
|
---|
| 88 | * from a tag value.
|
---|
| 89 | */
|
---|
[5342] | 90 | TagKeyReference tkr = c.get(TEXT, null, TagKeyReference.class, true);
|
---|
[4007] | 91 | if (tkr != null)
|
---|
| 92 | return new TagLookupCompositionStrategy(tkr.key);
|
---|
| 93 |
|
---|
| 94 | /*
|
---|
[7083] | 95 | * Check whether the label composition strategy is given by a keyword
|
---|
[4007] | 96 | */
|
---|
[5342] | 97 | Keyword keyword = c.get(TEXT, null, Keyword.class, true);
|
---|
[7083] | 98 | if (Keyword.AUTO.equals(keyword))
|
---|
[3987] | 99 | return AUTO_LABEL_COMPOSITION_STRATEGY;
|
---|
[4007] | 100 |
|
---|
| 101 | /*
|
---|
| 102 | * Do we have a static text label?
|
---|
| 103 | */
|
---|
[5342] | 104 | String text = c.get(TEXT, null, String.class, true);
|
---|
[4007] | 105 | if (text != null)
|
---|
| 106 | return new StaticLabelCompositionStrategy(text);
|
---|
| 107 | return defaultAnnotate ? AUTO_LABEL_COMPOSITION_STRATEGY : null;
|
---|
[3987] | 108 | }
|
---|
[3880] | 109 |
|
---|
[3987] | 110 | /**
|
---|
| 111 | * Builds a text element from style properties in {@code c} and the
|
---|
| 112 | * default text color {@code defaultTextColor}
|
---|
[6070] | 113 | *
|
---|
[7402] | 114 | * @param env the environment
|
---|
[3987] | 115 | * @param defaultTextColor the default text color. Must not be null.
|
---|
[4074] | 116 | * @param defaultAnnotate true, if a text label shall be rendered by default, even if the style sheet
|
---|
| 117 | * doesn't include respective style declarations
|
---|
[3987] | 118 | * @return the text element or null, if the style properties don't include
|
---|
| 119 | * properties for text rendering
|
---|
[8291] | 120 | * @throws IllegalArgumentException if {@code defaultTextColor} is null
|
---|
[3987] | 121 | */
|
---|
[9278] | 122 | public static TextLabel create(Environment env, Color defaultTextColor, boolean defaultAnnotate) {
|
---|
[3987] | 123 | CheckParameterUtil.ensureParameterNotNull(defaultTextColor);
|
---|
[7383] | 124 | Cascade c = env.mc.getCascade(env.layer);
|
---|
[3987] | 125 |
|
---|
[3992] | 126 | LabelCompositionStrategy strategy = buildLabelCompositionStrategy(c, defaultAnnotate);
|
---|
[4281] | 127 | if (strategy == null) return null;
|
---|
[7383] | 128 | String s = strategy.compose(env.osm);
|
---|
| 129 | if (s == null) return null;
|
---|
[9278] | 130 | Font font = StyleElement.getFont(c, s);
|
---|
[3882] | 131 |
|
---|
| 132 | float xOffset = 0;
|
---|
| 133 | float yOffset = 0;
|
---|
[8087] | 134 | float[] offset = c.get(TEXT_OFFSET, null, float[].class);
|
---|
[3882] | 135 | if (offset != null) {
|
---|
| 136 | if (offset.length == 1) {
|
---|
| 137 | yOffset = offset[0];
|
---|
| 138 | } else if (offset.length >= 2) {
|
---|
| 139 | xOffset = offset[0];
|
---|
| 140 | yOffset = offset[1];
|
---|
| 141 | }
|
---|
| 142 | }
|
---|
[8087] | 143 | xOffset = c.get(TEXT_OFFSET_X, xOffset, Float.class);
|
---|
| 144 | yOffset = c.get(TEXT_OFFSET_Y, yOffset, Float.class);
|
---|
[3987] | 145 |
|
---|
[8087] | 146 | Color color = c.get(TEXT_COLOR, defaultTextColor, Color.class);
|
---|
| 147 | float alpha = c.get(TEXT_OPACITY, 1f, Float.class);
|
---|
[3979] | 148 | color = new Color(color.getRed(), color.getGreen(),
|
---|
[10748] | 149 | color.getBlue(), Utils.colorFloat2int(alpha));
|
---|
[3979] | 150 |
|
---|
[8087] | 151 | Float haloRadius = c.get(TEXT_HALO_RADIUS, null, Float.class);
|
---|
[3979] | 152 | if (haloRadius != null && haloRadius <= 0) {
|
---|
| 153 | haloRadius = null;
|
---|
| 154 | }
|
---|
| 155 | Color haloColor = null;
|
---|
| 156 | if (haloRadius != null) {
|
---|
[8087] | 157 | haloColor = c.get(TEXT_HALO_COLOR, Utils.complement(color), Color.class);
|
---|
| 158 | float haloAlpha = c.get(TEXT_HALO_OPACITY, 1f, Float.class);
|
---|
[3979] | 159 | haloColor = new Color(haloColor.getRed(), haloColor.getGreen(),
|
---|
[10748] | 160 | haloColor.getBlue(), Utils.colorFloat2int(haloAlpha));
|
---|
[3979] | 161 | }
|
---|
| 162 |
|
---|
[9278] | 163 | return new TextLabel(strategy, font, (int) xOffset, -(int) yOffset, color, haloRadius, haloColor);
|
---|
[3880] | 164 | }
|
---|
| 165 |
|
---|
[3987] | 166 | /**
|
---|
| 167 | * Replies the label to be rendered for the primitive {@code osm}.
|
---|
[6070] | 168 | *
|
---|
[4310] | 169 | * @param osm the OSM object
|
---|
[3987] | 170 | * @return the label, or null, if {@code osm} is null or if no label can be
|
---|
| 171 | * derived for {@code osm}
|
---|
| 172 | */
|
---|
| 173 | public String getString(OsmPrimitive osm) {
|
---|
| 174 | if (labelCompositionStrategy == null) return null;
|
---|
| 175 | return labelCompositionStrategy.compose(osm);
|
---|
| 176 | }
|
---|
| 177 |
|
---|
[3880] | 178 | @Override
|
---|
[3987] | 179 | public String toString() {
|
---|
[4003] | 180 | return "TextElement{" + toStringImpl() + '}';
|
---|
| 181 | }
|
---|
| 182 |
|
---|
| 183 | protected String toStringImpl() {
|
---|
[8390] | 184 | StringBuilder sb = new StringBuilder(96);
|
---|
| 185 | sb.append("labelCompositionStrategy=").append(labelCompositionStrategy)
|
---|
| 186 | .append(" font=").append(font);
|
---|
[4003] | 187 | if (xOffset != 0) {
|
---|
[8390] | 188 | sb.append(" xOffset=").append(xOffset);
|
---|
[4003] | 189 | }
|
---|
| 190 | if (yOffset != 0) {
|
---|
[8390] | 191 | sb.append(" yOffset=").append(yOffset);
|
---|
[4003] | 192 | }
|
---|
[8390] | 193 | sb.append(" color=").append(Utils.toString(color));
|
---|
[4003] | 194 | if (haloRadius != null) {
|
---|
[8390] | 195 | sb.append(" haloRadius=").append(haloRadius)
|
---|
| 196 | .append(" haloColor=").append(haloColor);
|
---|
[4003] | 197 | }
|
---|
[3987] | 198 | return sb.toString();
|
---|
[3880] | 199 | }
|
---|
| 200 |
|
---|
| 201 | @Override
|
---|
| 202 | public int hashCode() {
|
---|
[9371] | 203 | return Objects.hash(labelCompositionStrategy, font, xOffset, yOffset, color, haloRadius, haloColor);
|
---|
[3880] | 204 | }
|
---|
| 205 |
|
---|
[3987] | 206 | @Override
|
---|
| 207 | public boolean equals(Object obj) {
|
---|
[9371] | 208 | if (this == obj) return true;
|
---|
| 209 | if (obj == null || getClass() != obj.getClass()) return false;
|
---|
| 210 | TextLabel textLabel = (TextLabel) obj;
|
---|
| 211 | return xOffset == textLabel.xOffset &&
|
---|
| 212 | yOffset == textLabel.yOffset &&
|
---|
| 213 | Objects.equals(labelCompositionStrategy, textLabel.labelCompositionStrategy) &&
|
---|
| 214 | Objects.equals(font, textLabel.font) &&
|
---|
| 215 | Objects.equals(color, textLabel.color) &&
|
---|
| 216 | Objects.equals(haloRadius, textLabel.haloRadius) &&
|
---|
| 217 | Objects.equals(haloColor, textLabel.haloColor);
|
---|
[3899] | 218 | }
|
---|
[3880] | 219 | }
|
---|