Ticket #6107: mapcss-patch-2.txt

File mapcss-patch-2.txt, 42.3 KB (added by anonymous, 15 years ago)

Replaces former mapcss-patch.txt

Line 
1Index: src/org/openstreetmap/josm/data/osm/visitor/paint/MapPainter.java
2===================================================================
3--- src/org/openstreetmap/josm/data/osm/visitor/paint/MapPainter.java (revision 3986)
4+++ src/org/openstreetmap/josm/data/osm/visitor/paint/MapPainter.java (working copy)
5@@ -38,12 +38,11 @@
6 import org.openstreetmap.josm.gui.NavigatableComponent;
7 import org.openstreetmap.josm.gui.mappaint.NodeElemStyle;
8 import org.openstreetmap.josm.gui.mappaint.NodeElemStyle.HorizontalTextAlignment;
9-import org.openstreetmap.josm.gui.mappaint.NodeElemStyle.Symbol;
10 import org.openstreetmap.josm.gui.mappaint.NodeElemStyle.NodeTextElement;
11+import org.openstreetmap.josm.gui.mappaint.NodeElemStyle.Symbol;
12 import org.openstreetmap.josm.gui.mappaint.NodeElemStyle.VerticalTextAlignment;
13 import org.openstreetmap.josm.gui.mappaint.TextElement;
14 import org.openstreetmap.josm.tools.ImageProvider;
15-import org.openstreetmap.josm.tools.LanguageInfo;
16 import org.openstreetmap.josm.tools.Pair;
17
18 public class MapPainter {
19@@ -73,8 +72,6 @@
20
21 private final boolean leftHandTraffic;
22
23- private final Collection<String> regionalNameOrder;
24-
25 private static final double PHI = Math.toRadians(20);
26 private static final double cosPHI = Math.cos(PHI);
27 private static final double sinPHI = Math.sin(PHI);
28@@ -103,8 +100,6 @@
29 this.virtualNodeSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70);
30 this.segmentNumberSpace = Main.pref.getInteger("mappaint.segmentnumber.space", 40);
31
32- String[] names = {"name:" + LanguageInfo.getJOSMLocaleCode(), "name", "int_name", "ref", "operator", "brand", "addr:housenumber"};
33- this.regionalNameOrder = Main.pref.getCollection("mappaint.nameOrder", Arrays.asList(names));
34 this.circum = circum;
35 this.leftHandTraffic = leftHandTraffic;
36 }
37@@ -117,7 +112,7 @@
38 * e.g. oneway street or waterway
39 * @param onewayReversed for oneway=-1 and similar
40 */
41- public void drawWay(Way way, Color color, BasicStroke line, BasicStroke dashes, Color dashedColor,
42+ public void drawWay(Way way, Color color, BasicStroke line, BasicStroke dashes, Color dashedColor,
43 TextElement text, boolean showOrientation, boolean showHeadArrowOnly,
44 boolean showOneway, boolean onewayReversed) {
45
46@@ -252,7 +247,7 @@
47 private void drawTextOnPath(Way way, TextElement text) {
48 if (text == null)
49 return;
50- String name = text.getString(way, this);
51+ String name = text.getString(way);
52 if (name == null || name.equals(""))
53 return;
54
55@@ -291,8 +286,8 @@
56 double tStart;
57
58 if (p1[0] < p2[0] &&
59- p1[2] < Math.PI/2 &&
60- p1[2] > -Math.PI/2) {
61+ p1[2] < Math.PI/2 &&
62+ p1[2] > -Math.PI/2) {
63 angleOffset = 0;
64 offsetSign = 1;
65 tStart = t1;
66@@ -346,8 +341,8 @@
67 continue;
68 }
69 return new double[] {poly.xpoints[i-1]+(totalLen - curLen)/segLen*dx,
70- poly.ypoints[i-1]+(totalLen - curLen)/segLen*dy,
71- Math.atan2(dy, dx)};
72+ poly.ypoints[i-1]+(totalLen - curLen)/segLen*dy,
73+ Math.atan2(dy, dx)};
74 }
75 return null;
76 }
77@@ -494,9 +489,12 @@
78 if (!isShowNames() || text == null)
79 return;
80
81- String s = text.textKey == null ? getNodeName(n) : n.get(text.textKey);
82- if (s == null)
83- return;
84+ /*
85+ * abort if we can't compose the label to be rendered
86+ */
87+ if (text.labelCompositionStrategy == null) return;
88+ String s = text.labelCompositionStrategy.compose(n);
89+ if (s == null) return;
90
91 Font defaultFont = g.getFont();
92 g.setFont(text.font);
93@@ -597,9 +595,12 @@
94 }
95
96 if (text != null && isShowNames()) {
97- String name = text.textKey == null ? getAreaName(osm) : osm.get(text.textKey);
98- if (name == null)
99- return;
100+ /*
101+ * abort if we can't compose the label to be rendered
102+ */
103+ if (text.labelCompositionStrategy == null) return;
104+ String name = text.labelCompositionStrategy.compose(osm);
105+ if (name == null) return;
106
107 Rectangle pb = polygon.getBounds();
108 FontMetrics fontMetrics = g.getFontMetrics(orderFont); // if slow, use cache
109@@ -930,34 +931,6 @@
110 }
111 }
112
113- //TODO Not a good place for this method
114- public String getNodeName(Node n) {
115- String name = null;
116- if (n.hasKeys()) {
117- for (String rn : regionalNameOrder) {
118- name = n.get(rn);
119- if (name != null) {
120- break;
121- }
122- }
123- }
124- return name;
125- }
126-
127- //TODO Not a good place for this method
128- public String getAreaName(OsmPrimitive w) {
129- String name = null;
130- if (w.hasKeys()) {
131- for (String rn : regionalNameOrder) {
132- name = w.get(rn);
133- if (name != null) {
134- break;
135- }
136- }
137- }
138- return name;
139- }
140-
141 public boolean isInactive() {
142 return inactive;
143 }
144Index: src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java
145===================================================================
146--- src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java (revision 3986)
147+++ src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java (working copy)
148@@ -16,7 +16,7 @@
149 public float z_index;
150 public float object_z_index;
151 public boolean isModifier; // false, if style can serve as main style for the
152- // primitive; true, if it is a highlight or modifier
153+ // primitive; true, if it is a highlight or modifier
154
155 public ElemStyle(float z_index, float object_z_index, boolean isModifier) {
156 this.z_index = z_index;
157@@ -63,7 +63,7 @@
158 String name = c.get("font-family", Main.pref.get("mappaint.font", "Helvetica"), String.class);
159 float size = c.get("font-size", (float) Main.pref.getInteger("mappaint.fontsize", 8), Float.class);
160 int weight = Font.PLAIN;
161- Keyword weightKW = c.get("font-wheight", null, Keyword.class);
162+ Keyword weightKW = c.get("font-weight", null, Keyword.class);
163 if (weightKW != null && equal(weightKW, "bold")) {
164 weight = Font.BOLD;
165 }
166Index: src/org/openstreetmap/josm/gui/mappaint/LabelCompositionStrategy.java
167===================================================================
168--- src/org/openstreetmap/josm/gui/mappaint/LabelCompositionStrategy.java (revision 0)
169+++ src/org/openstreetmap/josm/gui/mappaint/LabelCompositionStrategy.java (revision 0)
170@@ -0,0 +1,244 @@
171+// License: GPL. For details, see LICENSE file.
172+package org.openstreetmap.josm.gui.mappaint;
173+import java.util.ArrayList;
174+import java.util.Arrays;
175+import java.util.Collections;
176+import java.util.List;
177+
178+import org.openstreetmap.josm.Main;
179+import org.openstreetmap.josm.data.osm.OsmPrimitive;
180+import org.openstreetmap.josm.tools.LanguageInfo;
181+
182+/**
183+ * <p>Provides an abstract parent class and three concrete sub classes for various
184+ * strategies on how to compose the text label which can be rendered close to a node
185+ * or within an area in an OSM map.</p>
186+ *
187+ * <p>The three strategies below support three rules for composing a label:
188+ * <ul>
189+ * <li>{@link StaticLabelCompositionStrategy} - the label is given by a static text
190+ * specified in the MapCSS style file</li>
191+ *
192+ * <li>{@link TagLookupCompositionStrategy} - the label is given by the content of a
193+ * tag whose name specified in the MapCSS style file</li>
194+ *
195+ * <li>{@link DeriveLabelFromNameTagsCompositionStrategy} - the label is given by the value
196+ * of one
197+ * of the configured "name tags". The list of relevant name tags can be configured
198+ * in the JOSM preferences
199+ * content of a tag whose name specified in the MapCSS style file, see the preference
200+ * option <tt>mappaint.nameOrder</tt>.</li>
201+ * </ul>
202+ * </p>
203+ *
204+ */
205+public abstract class LabelCompositionStrategy {
206+
207+ static public class StaticLabelCompositionStrategy extends LabelCompositionStrategy {
208+ private String defaultLabel;
209+ public StaticLabelCompositionStrategy(String defaultLabel){
210+ this.defaultLabel = defaultLabel;
211+ }
212+
213+ @Override
214+ public String compose(OsmPrimitive primitive) {
215+ return defaultLabel;
216+ }
217+
218+ public String getDefaultLabel() {
219+ return defaultLabel;
220+ }
221+
222+ @Override
223+ public String toString() {
224+ return "{" + getClass().getSimpleName() + " defaultLabel=" + defaultLabel + "}";
225+ }
226+
227+ @Override
228+ public int hashCode() {
229+ final int prime = 31;
230+ int result = 1;
231+ result = prime * result + ((defaultLabel == null) ? 0 : defaultLabel.hashCode());
232+ return result;
233+ }
234+
235+ @Override
236+ public boolean equals(Object obj) {
237+ if (this == obj)
238+ return true;
239+ if (obj == null)
240+ return false;
241+ if (getClass() != obj.getClass())
242+ return false;
243+ StaticLabelCompositionStrategy other = (StaticLabelCompositionStrategy) obj;
244+ if (defaultLabel == null) {
245+ if (other.defaultLabel != null)
246+ return false;
247+ } else if (!defaultLabel.equals(other.defaultLabel))
248+ return false;
249+ return true;
250+ }
251+ }
252+
253+ static public class TagLookupCompositionStrategy extends LabelCompositionStrategy {
254+
255+ private String defaultLabelTag;
256+ public TagLookupCompositionStrategy(String defaultLabelTag){
257+ if (defaultLabelTag != null) {
258+ defaultLabelTag = defaultLabelTag.trim();
259+ if (defaultLabelTag.isEmpty()) {
260+ defaultLabelTag = null;
261+ }
262+ }
263+ this.defaultLabelTag = defaultLabelTag;
264+ }
265+
266+ @Override
267+ public String compose(OsmPrimitive primitive) {
268+ if (defaultLabelTag == null) return null;
269+ if (primitive == null) return null;
270+ return primitive.get(defaultLabelTag);
271+ }
272+
273+ public String getDefaultLabelTag() {
274+ return defaultLabelTag;
275+ }
276+
277+ @Override
278+ public String toString() {
279+ return "{" + getClass().getSimpleName() + " defaultLabelTag=" + defaultLabelTag + "}";
280+ }
281+
282+ @Override
283+ public int hashCode() {
284+ final int prime = 31;
285+ int result = 1;
286+ result = prime * result + ((defaultLabelTag == null) ? 0 : defaultLabelTag.hashCode());
287+ return result;
288+ }
289+
290+ @Override
291+ public boolean equals(Object obj) {
292+ if (this == obj)
293+ return true;
294+ if (obj == null)
295+ return false;
296+ if (getClass() != obj.getClass())
297+ return false;
298+ TagLookupCompositionStrategy other = (TagLookupCompositionStrategy) obj;
299+ if (defaultLabelTag == null) {
300+ if (other.defaultLabelTag != null)
301+ return false;
302+ } else if (!defaultLabelTag.equals(other.defaultLabelTag))
303+ return false;
304+ return true;
305+ }
306+ }
307+
308+ static public class DeriveLabelFromNameTagsCompositionStrategy extends LabelCompositionStrategy {
309+
310+ /**
311+ * The list of default name tags from which a label candidate is derived.
312+ */
313+ static public final String[] DEFAULT_NAME_TAGS = {
314+ "name:" + LanguageInfo.getJOSMLocaleCode(),
315+ "name",
316+ "int_name",
317+ "ref",
318+ "operator",
319+ "brand",
320+ "addr:housenumber"
321+ };
322+
323+ private List<String> nameTags = new ArrayList<String>();
324+
325+ /**
326+ * <p>Creates the strategy and initializes its name tags from the preferences.</p>
327+ *
328+ * <p><strong>Note:</strong> If the list of name tags in the preferences changes, strategy instances
329+ * are not notified. It's up to the client to listen to preference changes and
330+ * invoke {@link #initNameTagsFromPreferences()} accordingly.</p>
331+ *
332+ */
333+ public DeriveLabelFromNameTagsCompositionStrategy() {
334+ initNameTagsFromPreferences();
335+ }
336+
337+ /**
338+ * Sets the name tags to be looked up in order to build up the label
339+ *
340+ * @param nameTags the name tags. null values are ignore.
341+ */
342+ public void setNameTags(List<String> nameTags){
343+ if (nameTags == null) {
344+ nameTags = Collections.emptyList();
345+ }
346+ this.nameTags = new ArrayList<String>();
347+ for(String tag: nameTags) {
348+ if (tag == null) {
349+ continue;
350+ }
351+ tag = tag.trim();
352+ if (tag.isEmpty()) {
353+ continue;
354+ }
355+ this.nameTags.add(tag);
356+ }
357+ }
358+
359+ /**
360+ * Replies an unmodifiable list of the name tags used to compose the label.
361+ *
362+ * @return the list of name tags
363+ */
364+ public List<String> getNameTags() {
365+ return Collections.unmodifiableList(nameTags);
366+ }
367+
368+ /**
369+ * Initializes the name tags to use from a list of default name tags (see
370+ * {@link #DEFAULT_NAME_TAGS}) and from name tags configured in the preferences
371+ * using the preference key <tt>mappaint.nameOrder</tt>.
372+ */
373+ public void initNameTagsFromPreferences() {
374+ if (Main.pref == null){
375+ this.nameTags = new ArrayList<String>(Arrays.asList(DEFAULT_NAME_TAGS));
376+ } else {
377+ this.nameTags = new ArrayList<String>(
378+ Main.pref.getCollection("mappaint.nameOrder", Arrays.asList(DEFAULT_NAME_TAGS))
379+ );
380+ }
381+ }
382+
383+ private String getPrimitiveName(OsmPrimitive n) {
384+ String name = null;
385+ if (!n.hasKeys()) return null;
386+ for (String rn : nameTags) {
387+ name = n.get(rn);
388+ if (name != null) return name;
389+ }
390+ return null;
391+ }
392+
393+ @Override
394+ public String compose(OsmPrimitive primitive) {
395+ if (primitive == null) return null;
396+ return getPrimitiveName(primitive);
397+ }
398+
399+ @Override
400+ public String toString() {
401+ return "{" + getClass().getSimpleName() +"}";
402+ }
403+ }
404+
405+ /**
406+ * Replies the text value to be rendered as label for the primitive {@code primitive}.
407+ *
408+ * @param primitive the primitive
409+ *
410+ * @return the text value to be rendered or null, if primitive is null or
411+ * if no suitable value could be composed
412+ */
413+ abstract public String compose(OsmPrimitive primitive);
414+}
415Index: src/org/openstreetmap/josm/gui/mappaint/MultiCascade.java
416===================================================================
417--- src/org/openstreetmap/josm/gui/mappaint/MultiCascade.java (revision 3986)
418+++ src/org/openstreetmap/josm/gui/mappaint/MultiCascade.java (working copy)
419@@ -6,13 +6,15 @@
420 import java.util.Map;
421 import java.util.Map.Entry;
422
423+import org.openstreetmap.josm.tools.CheckParameterUtil;
424+
425 /**
426 * Several layers / cascades, e.g. one for the main Line and one for each overlay.
427 * The range is (0,Infinity) at first and it shrinks in the process when
428 * StyleSources apply zoom level dependent properties.
429 */
430 public class MultiCascade {
431-
432+
433 private Map<String, Cascade> layers;
434 public Range range;
435
436@@ -27,8 +29,7 @@
437 * a clone of the "*" layer, if it exists.
438 */
439 public Cascade getOrCreateCascade(String layer) {
440- if (layer == null)
441- throw new IllegalArgumentException();
442+ CheckParameterUtil.ensureParameterNotNull(layer);
443 Cascade c = layers.get(layer);
444 if (c == null) {
445 if (layers.containsKey("*")) {
446Index: src/org/openstreetmap/josm/gui/mappaint/NodeElemStyle.java
447===================================================================
448--- src/org/openstreetmap/josm/gui/mappaint/NodeElemStyle.java (revision 3986)
449+++ src/org/openstreetmap/josm/gui/mappaint/NodeElemStyle.java (working copy)
450@@ -25,6 +25,7 @@
451 * applies for Nodes and turn restriction relations
452 */
453 public class NodeElemStyle extends ElemStyle {
454+ //static private final Logger logger = Logger.getLogger(NodeElemStyle.class.getName());
455
456 public ImageIcon icon;
457 public int iconAlpha;
458@@ -62,10 +63,10 @@
459 return false;
460 final Symbol other = (Symbol) obj;
461 return symbol == other.symbol &&
462- size == other.size &&
463- equal(stroke, other.stroke) &&
464- equal(strokeColor, other.strokeColor) &&
465- equal(fillColor, other.fillColor);
466+ size == other.size &&
467+ equal(stroke, other.stroke) &&
468+ equal(strokeColor, other.strokeColor) &&
469+ equal(fillColor, other.fillColor);
470 }
471
472 @Override
473@@ -82,8 +83,8 @@
474 @Override
475 public String toString() {
476 return "symbol=" + symbol + " size=" + size +
477- (stroke != null ? (" stroke=" + stroke + " strokeColor=" + strokeColor) : "") +
478- (fillColor != null ? (" fillColor=" + fillColor) : "");
479+ (stroke != null ? (" stroke=" + stroke + " strokeColor=" + strokeColor) : "") +
480+ (fillColor != null ? (" fillColor=" + fillColor) : "");
481 }
482 }
483
484@@ -107,7 +108,7 @@
485 return false;
486 final NodeTextElement other = (NodeTextElement) obj;
487 return hAlign == other.hAlign &&
488- vAlign == other.vAlign;
489+ vAlign == other.vAlign;
490 }
491
492 @Override
493@@ -162,9 +163,6 @@
494 symbol = createSymbol(env);
495 }
496
497- if (icon == null && symbol == null && !allowOnlyText)
498- return null;
499-
500 NodeTextElement text = null;
501 TextElement te = TextElement.create(c, PaintColors.TEXT.get());
502 if (te != null) {
503@@ -192,7 +190,7 @@
504 }
505 text = new NodeTextElement(te, hAlign, vAlign);
506 }
507-
508+
509 return new NodeElemStyle(c, icon, iconAlpha, symbol, text);
510 }
511
512@@ -224,7 +222,7 @@
513 shape = SymbolShape.DECAGON;
514 } else
515 return null;
516-
517+
518 Float sizeOnDefault = c_def.get("symbol-size", null, Float.class);
519 if (sizeOnDefault != null && sizeOnDefault <= 0) {
520 sizeOnDefault = null;
521@@ -258,8 +256,9 @@
522 }
523
524 Color fillColor = c.get("symbol-fill-color", null, Color.class);
525- if (stroke == null && fillColor == null)
526+ if (stroke == null && fillColor == null) {
527 fillColor = Color.BLUE;
528+ }
529
530 if (fillColor != null) {
531 float fillAlpha = c.get("symbol-fill-opacity", 1f, Float.class);
532@@ -335,14 +334,14 @@
533 }
534
535 final int size = Utils.max((selected ? settings.getSelectedNodeSize() : 0),
536- (n.isTagged() ? settings.getTaggedNodeSize() : 0),
537- (isConnection ? settings.getConnectionNodeSize() : 0),
538- settings.getUnselectedNodeSize());
539+ (n.isTagged() ? settings.getTaggedNodeSize() : 0),
540+ (isConnection ? settings.getConnectionNodeSize() : 0),
541+ settings.getUnselectedNodeSize());
542
543 final boolean fill = (selected && settings.isFillSelectedNode()) ||
544- (n.isTagged() && settings.isFillTaggedNode()) ||
545- (isConnection && settings.isFillConnectionNode()) ||
546- settings.isFillUnselectedNode();
547+ (n.isTagged() && settings.isFillTaggedNode()) ||
548+ (isConnection && settings.isFillConnectionNode()) ||
549+ settings.isFillUnselectedNode();
550
551 painter.drawNode(n, color, size, fill, text);
552 }
553@@ -394,8 +393,8 @@
554 @Override
555 public String toString() {
556 return "NodeElemStyle{" + super.toString() +
557- (icon != null ? ("icon=" + icon + " iconAlpha=" + iconAlpha) : "") +
558- (symbol != null ? (" symbol=[" + symbol + "]") : "") + '}';
559+ (icon != null ? ("icon=" + icon + " iconAlpha=" + iconAlpha) : "") +
560+ (symbol != null ? (" symbol=[" + symbol + "]") : "") + '}';
561 }
562
563 }
564Index: src/org/openstreetmap/josm/gui/mappaint/TextElement.java
565===================================================================
566--- src/org/openstreetmap/josm/gui/mappaint/TextElement.java (revision 3986)
567+++ src/org/openstreetmap/josm/gui/mappaint/TextElement.java (working copy)
568@@ -1,31 +1,52 @@
569 // License: GPL. For details, see LICENSE file.
570 package org.openstreetmap.josm.gui.mappaint;
571
572-import static org.openstreetmap.josm.tools.Utils.equal;
573-
574 import java.awt.Color;
575 import java.awt.Font;
576+
577 import org.openstreetmap.josm.data.osm.OsmPrimitive;
578-import org.openstreetmap.josm.data.osm.visitor.paint.MapPainter;
579-
580+import org.openstreetmap.josm.gui.mappaint.LabelCompositionStrategy.DeriveLabelFromNameTagsCompositionStrategy;
581+import org.openstreetmap.josm.gui.mappaint.LabelCompositionStrategy.TagLookupCompositionStrategy;
582 import org.openstreetmap.josm.tools.CheckParameterUtil;
583 import org.openstreetmap.josm.tools.Utils;
584
585+/**
586+ * Represents the rendering style for a textual label placed somewhere on the map.
587+ *
588+ */
589 public class TextElement {
590- // textKey == null means automatic generation of text string, otherwise
591- // the corresponding tag value is used
592- public String textKey;
593+ //static private final Logger logger = Logger.getLogger(TextElement.class.getName());
594+
595+ static private final LabelCompositionStrategy AUTO_LABEL_COMPOSITION_STRATEGY = new DeriveLabelFromNameTagsCompositionStrategy();
596+
597+ /** the font to be used when rendering*/
598 public Font font;
599 public int xOffset;
600 public int yOffset;
601 public Color color;
602 public Float haloRadius;
603 public Color haloColor;
604+ /** the strategy for building the actual label value for a given a {@link OsmPrimitive}.
605+ * Check for null before accessing.
606+ */
607+ public LabelCompositionStrategy labelCompositionStrategy;
608
609- public TextElement(String textKey, Font font, int xOffset, int yOffset, Color color, Float haloRadius, Color haloColor) {
610+ /**
611+ * Creates a new text element
612+ *
613+ * @param strategy the strategy indicating how the text is composed for a specific {@link OsmPrimitive} to be rendered.
614+ * If null, no label is rendered.
615+ * @param font the font to be used. Must not be null.
616+ * @param xOffset
617+ * @param yOffset
618+ * @param color the color to be used. Must not be null
619+ * @param haloRadius
620+ * @param haloColor
621+ */
622+ public TextElement(LabelCompositionStrategy strategy, Font font, int xOffset, int yOffset, Color color, Float haloRadius, Color haloColor) {
623 CheckParameterUtil.ensureParameterNotNull(font);
624 CheckParameterUtil.ensureParameterNotNull(color);
625- this.textKey = textKey;
626+ labelCompositionStrategy = strategy;
627 this.font = font;
628 this.xOffset = xOffset;
629 this.yOffset = yOffset;
630@@ -34,8 +55,13 @@
631 this.haloColor = haloColor;
632 }
633
634+ /**
635+ * Copy constructor
636+ *
637+ * @param other the other element.
638+ */
639 public TextElement(TextElement other) {
640- this.textKey = other.textKey;
641+ this.labelCompositionStrategy = other.labelCompositionStrategy;
642 this.font = other.font;
643 this.xOffset = other.xOffset;
644 this.yOffset = other.yOffset;
645@@ -44,17 +70,39 @@
646 this.haloRadius = other.haloRadius;
647 }
648
649- public static TextElement create(Cascade c, Color defTextColor) {
650-
651- String textKey = null;
652+ /**
653+ * Derives a suitable label composition strategy from the style properties in
654+ * {@code c}.
655+ *
656+ * @param c the style properties
657+ * @return the label composition strategy
658+ */
659+ protected static LabelCompositionStrategy buildLabelCompositionStrategy(Cascade c){
660 Keyword textKW = c.get("text", null, Keyword.class, true);
661 if (textKW == null) {
662- textKey = c.get("text", null, String.class);
663- if (textKey == null)
664- return null;
665- } else if (!textKW.val.equals("auto"))
666- return null;
667+ String textKey = c.get("text", null, String.class);
668+ if (textKey == null) return null;
669+ return new TagLookupCompositionStrategy(textKey);
670+ } else if (textKW.val.equals("auto"))
671+ return AUTO_LABEL_COMPOSITION_STRATEGY;
672+ else
673+ return new TagLookupCompositionStrategy(textKW.val);
674+ }
675
676+ /**
677+ * Builds a text element from style properties in {@code c} and the
678+ * default text color {@code defaultTextColor}
679+ *
680+ * @param c the style properties
681+ * @param defaultTextColor the default text color. Must not be null.
682+ * @return the text element or null, if the style properties don't include
683+ * properties for text rendering
684+ * @throws IllegalArgumentException thrown if {@code defaultTextColor} is null
685+ */
686+ public static TextElement create(Cascade c, Color defaultTextColor) throws IllegalArgumentException{
687+ CheckParameterUtil.ensureParameterNotNull(defaultTextColor, "defaultTextColor");
688+
689+ LabelCompositionStrategy strategy = buildLabelCompositionStrategy(c);
690 Font font = ElemStyle.getFont(c);
691
692 float xOffset = 0;
693@@ -70,8 +118,8 @@
694 }
695 xOffset = c.get("text-offset-x", xOffset, Float.class);
696 yOffset = c.get("text-offset-y", yOffset, Float.class);
697-
698- Color color = c.get("text-color", defTextColor, Color.class);
699+
700+ Color color = c.get("text-color", defaultTextColor, Color.class);
701 float alpha = c.get("text-opacity", 1f, Float.class);
702 color = new Color(color.getRed(), color.getGreen(),
703 color.getBlue(), Utils.color_float2int(alpha));
704@@ -88,42 +136,86 @@
705 haloColor.getBlue(), Utils.color_float2int(haloAlpha));
706 }
707
708- return new TextElement(textKey, font, (int) xOffset, - (int) yOffset, color, haloRadius, haloColor);
709+ return new TextElement(strategy, font, (int) xOffset, - (int) yOffset, color, haloRadius, haloColor);
710 }
711
712+ /**
713+ * Replies the label to be rendered for the primitive {@code osm}.
714+ *
715+ * @param osm the the OSM object
716+ * @return the label, or null, if {@code osm} is null or if no label can be
717+ * derived for {@code osm}
718+ */
719+ public String getString(OsmPrimitive osm) {
720+ if (labelCompositionStrategy == null) return null;
721+ return labelCompositionStrategy.compose(osm);
722+ }
723+
724 @Override
725- public boolean equals(Object obj) {
726- if (obj == null || getClass() != obj.getClass())
727- return false;
728- final TextElement other = (TextElement) obj;
729- return equal(textKey, other.textKey) &&
730- equal(font, other.font) &&
731- xOffset == other.xOffset &&
732- yOffset == other.yOffset &&
733- equal(color, other.color) &&
734- equal(haloRadius, other.haloRadius) &&
735- equal(haloColor, other.haloColor);
736+ public String toString() {
737+ StringBuilder sb = new StringBuilder();
738+ sb.append("{TextElement ");
739+ sb.append("strategy=");
740+ sb.append(labelCompositionStrategy == null ? "null" : labelCompositionStrategy.toString());
741+ sb.append("}");
742+ return sb.toString();
743 }
744
745+ /* -------------------------------------------------------------------------------- */
746+ /* equals and hashCode (generated by Eclipse, regenerate if necessary) */
747+ /* -------------------------------------------------------------------------------- */
748 @Override
749 public int hashCode() {
750- int hash = 3;
751- hash = 79 * hash + (textKey != null ? textKey.hashCode() : 0);
752- hash = 79 * hash + font.hashCode();
753- hash = 79 * hash + xOffset;
754- hash = 79 * hash + yOffset;
755- hash = 79 * hash + color.hashCode();
756- hash = 79 * hash + (haloRadius != null ? Float.floatToIntBits(haloRadius) : 0);
757- hash = 79 * hash + (haloColor != null ? haloColor.hashCode() : 0);
758- return hash;
759+ final int prime = 31;
760+ int result = 1;
761+ result = prime * result + ((color == null) ? 0 : color.hashCode());
762+ result = prime * result + ((font == null) ? 0 : font.hashCode());
763+ result = prime * result + ((haloColor == null) ? 0 : haloColor.hashCode());
764+ result = prime * result + ((haloRadius == null) ? 0 : haloRadius.hashCode());
765+ result = prime * result + ((labelCompositionStrategy == null) ? 0 : labelCompositionStrategy.hashCode());
766+ result = prime * result + xOffset;
767+ result = prime * result + yOffset;
768+ return result;
769 }
770
771- public String getString(OsmPrimitive osm, MapPainter painter) {
772- if (textKey == null)
773- return painter.getAreaName(osm);
774- else
775- return osm.get(textKey);
776+ @Override
777+ public boolean equals(Object obj) {
778+ if (this == obj)
779+ return true;
780+ if (obj == null)
781+ return false;
782+ if (getClass() != obj.getClass())
783+ return false;
784+ TextElement other = (TextElement) obj;
785+ if (color == null) {
786+ if (other.color != null)
787+ return false;
788+ } else if (!color.equals(other.color))
789+ return false;
790+ if (font == null) {
791+ if (other.font != null)
792+ return false;
793+ } else if (!font.equals(other.font))
794+ return false;
795+ if (haloColor == null) {
796+ if (other.haloColor != null)
797+ return false;
798+ } else if (!haloColor.equals(other.haloColor))
799+ return false;
800+ if (haloRadius == null) {
801+ if (other.haloRadius != null)
802+ return false;
803+ } else if (!haloRadius.equals(other.haloRadius))
804+ return false;
805+ if (labelCompositionStrategy == null) {
806+ if (other.labelCompositionStrategy != null)
807+ return false;
808+ } else if (!labelCompositionStrategy.equals(other.labelCompositionStrategy))
809+ return false;
810+ if (xOffset != other.xOffset)
811+ return false;
812+ if (yOffset != other.yOffset)
813+ return false;
814+ return true;
815 }
816-
817-
818 }
819Index: src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java
820===================================================================
821--- src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java (revision 3986)
822+++ src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java (working copy)
823@@ -27,7 +27,8 @@
824 import org.openstreetmap.josm.tools.Utils;
825
826 public class MapCSSStyleSource extends StyleSource {
827-
828+ //static private final Logger logger = Logger.getLogger(MapCSSStyleSource.class.getName());
829+
830 final public List<MapCSSRule> rules;
831 private Color backgroundColorOverride;
832
833@@ -65,6 +66,7 @@
834 }
835 }
836
837+ @Override
838 public InputStream getSourceInputStream() throws IOException {
839 MirroredInputStream in = new MirroredInputStream(url);
840 InputStream zip = in.getZipEntry("mapcss", "style");
841@@ -107,26 +109,28 @@
842 Environment env = new Environment(n, mc, "default", this);
843
844 NEXT_RULE:
845- for (MapCSSRule r : rules) {
846- for (Selector s : r.selectors) {
847- if ((s instanceof GeneralSelector)) {
848- GeneralSelector gs = (GeneralSelector) s;
849- if (gs.base.equals(type))
850- {
851- for (Condition cnd : gs.conds) {
852- if (!cnd.applies(env))
853- continue NEXT_RULE;
854+ for (MapCSSRule r : rules) {
855+ for (Selector s : r.selectors) {
856+ if ((s instanceof GeneralSelector)) {
857+ GeneralSelector gs = (GeneralSelector) s;
858+ if (gs.base.equals(type))
859+ {
860+ for (Condition cnd : gs.conds) {
861+ if (!cnd.applies(env)) {
862+ continue NEXT_RULE;
863+ }
864+ }
865+ for (Instruction i : r.declaration) {
866+ i.execute(env);
867+ }
868 }
869- for (Instruction i : r.declaration) {
870- i.execute(env);
871- }
872 }
873 }
874 }
875- }
876 return mc.getCascade("default");
877 }
878
879+ @Override
880 public Color getBackgroundColorOverride() {
881 return backgroundColorOverride;
882 }
883@@ -159,7 +163,7 @@
884 i.execute(env);
885 }
886 }
887- }
888+ }
889 env.layer = sub;
890 for (Instruction i : r.declaration) {
891 i.execute(env);
892Index: test/data/styles/label-from-tag.mapcss
893===================================================================
894--- test/data/styles/label-from-tag.mapcss (revision 0)
895+++ test/data/styles/label-from-tag.mapcss (revision 0)
896@@ -0,0 +1,20 @@
897+/*
898+ * Simple test style sheet. Includes a style for nodes where the label is derived
899+ * from the value of a specific tag.
900+ *
901+ */
902+
903+meta {
904+ title: "Test style - Deriving labels from tags";
905+}
906+
907+canvas {
908+ background-color: #000000;
909+}
910+
911+node {
912+ text: my_label_tag; /* take the value of the tag 'my_label_tag' as text */
913+ text-color: white;
914+ font-size: 12;
915+}
916+
917Index: test/functional/org/openstreetmap/josm/fixtures/JOSMFixture.java
918===================================================================
919--- test/functional/org/openstreetmap/josm/fixtures/JOSMFixture.java (revision 3986)
920+++ test/functional/org/openstreetmap/josm/fixtures/JOSMFixture.java (working copy)
921@@ -10,7 +10,7 @@
922 import java.util.logging.Logger;
923
924 import org.openstreetmap.josm.Main;
925-import org.openstreetmap.josm.data.osm.DataSetMergerTest;
926+import org.openstreetmap.josm.data.Preferences;
927 import org.openstreetmap.josm.data.projection.Mercator;
928 import org.openstreetmap.josm.io.OsmApi;
929 import org.openstreetmap.josm.tools.I18n;
930@@ -39,7 +39,7 @@
931 // load properties
932 //
933 try {
934- testProperties.load(DataSetMergerTest.class.getResourceAsStream(testPropertiesResourceName));
935+ testProperties.load(JOSMFixture.class.getResourceAsStream(testPropertiesResourceName));
936 } catch(Exception e){
937 logger.log(Level.SEVERE, MessageFormat.format("failed to load property file ''{0}''", testPropertiesResourceName));
938 fail(MessageFormat.format("failed to load property file ''{0}''. \nMake sure the path ''$project_root/test/config'' is on the classpath.", testPropertiesResourceName));
939@@ -57,6 +57,7 @@
940 }
941 }
942 System.setProperty("josm.home", josmHome);
943+ Main.pref = new Preferences();
944 I18n.init();
945 // initialize the plaform hook, and
946 Main.determinePlatformHook();
947Index: test/unit/org/openstreetmap/josm/gui/mappaint/AllMappaintTests.groovy
948===================================================================
949--- test/unit/org/openstreetmap/josm/gui/mappaint/AllMappaintTests.groovy (revision 0)
950+++ test/unit/org/openstreetmap/josm/gui/mappaint/AllMappaintTests.groovy (revision 0)
951@@ -0,0 +1,15 @@
952+// License: GPL. For details, see LICENSE file.
953+package org.openstreetmap.josm.gui.mappaint
954+
955+import junit.framework.TestCase;
956+
957+import org.junit.runner.RunWith;
958+import org.junit.runners.Suite;
959+
960+@RunWith(Suite.class)
961+@Suite.SuiteClasses([
962+ LabelCompositionStrategyTest.class,
963+ MapCSSWithExtendedTextDirectivesTest.class
964+])
965+public class AllMappaintTests extends TestCase{}
966+
967Index: test/unit/org/openstreetmap/josm/gui/mappaint/LabelCompositionStrategyTest.groovy
968===================================================================
969--- test/unit/org/openstreetmap/josm/gui/mappaint/LabelCompositionStrategyTest.groovy (revision 0)
970+++ test/unit/org/openstreetmap/josm/gui/mappaint/LabelCompositionStrategyTest.groovy (revision 0)
971@@ -0,0 +1,66 @@
972+// License: GPL. For details, see LICENSE file.
973+package org.openstreetmap.josm.gui.mappaint
974+
975+import org.junit.*
976+import org.openstreetmap.josm.fixtures.JOSMFixture;
977+import org.openstreetmap.josm.gui.mappaint.LabelCompositionStrategy.DeriveLabelFromNameTagsCompositionStrategy
978+import org.openstreetmap.josm.gui.mappaint.LabelCompositionStrategy.StaticLabelCompositionStrategy;
979+import org.openstreetmap.josm.gui.mappaint.LabelCompositionStrategy.TagLookupCompositionStrategy
980+import org.openstreetmap.josm.data.osm.Node;
981+
982+class LabelCompositionStrategyTest {
983+
984+ @BeforeClass
985+ public static void createJOSMFixture(){
986+ JOSMFixture.createUnitTestFixture().init()
987+ }
988+
989+ @Test
990+ public void createStaticLabelCompositionStrategy() {
991+ def n = new Node()
992+
993+ def strat = new StaticLabelCompositionStrategy(null)
994+ assert strat.compose(n) == null
995+
996+ strat = new StaticLabelCompositionStrategy("a label")
997+ assert strat.compose(n) == "a label"
998+ }
999+
1000+ @Test
1001+ public void createTagLookupCompositionStrategy() {
1002+ def n = new Node()
1003+ n.put("my-tag", "my-value")
1004+
1005+ def strat = new TagLookupCompositionStrategy(null)
1006+ assert strat.compose(n) == null
1007+
1008+ strat = new TagLookupCompositionStrategy("name")
1009+ assert strat.compose(n) == null
1010+
1011+ strat = new TagLookupCompositionStrategy("my-tag")
1012+ assert strat.compose(n) == "my-value"
1013+ }
1014+
1015+ @Test
1016+ public void createDeriveLabelFromNameTagsCompositionStrategy() {
1017+ def n
1018+ def strat
1019+
1020+ strat = new DeriveLabelFromNameTagsCompositionStrategy()
1021+ strat.setNameTags(null)
1022+ assert strat.getNameTags() == []
1023+
1024+ strat = new DeriveLabelFromNameTagsCompositionStrategy()
1025+ strat.setNameTags(["name", "brand"])
1026+ assert strat.getNameTags() == ["name", "brand"]
1027+
1028+ n = new Node()
1029+ n.put("brand", "my brand")
1030+ assert strat.compose(n) == "my brand"
1031+
1032+ n = new Node()
1033+ n.put("name", "my name")
1034+ n.put("brand", "my brand")
1035+ assert strat.compose(n) == "my name"
1036+ }
1037+}
1038Index: test/unit/org/openstreetmap/josm/gui/mappaint/MapCSSWithExtendedTextDirectivesTest.groovy
1039===================================================================
1040--- test/unit/org/openstreetmap/josm/gui/mappaint/MapCSSWithExtendedTextDirectivesTest.groovy (revision 0)
1041+++ test/unit/org/openstreetmap/josm/gui/mappaint/MapCSSWithExtendedTextDirectivesTest.groovy (revision 0)
1042@@ -0,0 +1,58 @@
1043+// License: GPL. For details, see LICENSE file.
1044+package org.openstreetmap.josm.gui.mappaint
1045+
1046+import java.awt.Color;
1047+
1048+import org.junit.*;
1049+import org.openstreetmap.josm.fixtures.JOSMFixture
1050+import org.openstreetmap.josm.gui.mappaint.LabelCompositionStrategy.DeriveLabelFromNameTagsCompositionStrategy
1051+import org.openstreetmap.josm.gui.mappaint.LabelCompositionStrategy.StaticLabelCompositionStrategy
1052+import org.openstreetmap.josm.gui.mappaint.LabelCompositionStrategy.TagLookupCompositionStrategy
1053+class MapCSSWithExtendedTextDirectivesTest {
1054+
1055+
1056+ @BeforeClass
1057+ public static void createJOSMFixture(){
1058+ JOSMFixture.createUnitTestFixture().init()
1059+ }
1060+
1061+ @Test
1062+ public void createAutoTextElement() {
1063+ Cascade c = new Cascade()
1064+ c.put("text", new Keyword("auto"))
1065+
1066+ TextElement te = TextElement.create(c, Color.WHITE)
1067+ assert te.labelCompositionStrategy != null
1068+ assert te.labelCompositionStrategy instanceof DeriveLabelFromNameTagsCompositionStrategy
1069+ }
1070+
1071+ @Test
1072+ public void createTextElementComposingTextFromTag() {
1073+ Cascade c = new Cascade()
1074+ c.put("text", "my_name")
1075+
1076+ TextElement te = TextElement.create(c, Color.WHITE)
1077+ assert te.labelCompositionStrategy != null
1078+ assert te.labelCompositionStrategy instanceof TagLookupCompositionStrategy
1079+ assert te.labelCompositionStrategy.getDefaultLabelTag() == "my_name"
1080+ }
1081+
1082+ @Test
1083+ public void createTextElementComposingTextFromTag_2() {
1084+ Cascade c = new Cascade()
1085+ c.put("text", new Keyword("my_name"))
1086+
1087+ TextElement te = TextElement.create(c, Color.WHITE)
1088+ assert te.labelCompositionStrategy != null
1089+ assert te.labelCompositionStrategy instanceof TagLookupCompositionStrategy
1090+ assert te.labelCompositionStrategy.getDefaultLabelTag() == "my_name"
1091+ }
1092+
1093+ @Test
1094+ public void createNullStrategy() {
1095+ Cascade c = new Cascade()
1096+
1097+ TextElement te = TextElement.create(c, Color.WHITE)
1098+ assert te.labelCompositionStrategy == null
1099+ }
1100+}