source: josm/trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/MapPaintVisitor.java@ 2667

Last change on this file since 2667 was 2667, checked in by jttt, 14 years ago

Split MapPaintVisitor into two classes - MapPainter do the actual drawing while MapPaintVisitor choose style

  • Property svn:eol-style set to native
File size: 45.0 KB
Line 
1/* License: GPL. Copyright 2007 by Immanuel Scholz and others */
2package org.openstreetmap.josm.data.osm.visitor.paint;
3
4/* To enable debugging or profiling remove the double / signs */
5
6import static org.openstreetmap.josm.tools.I18n.tr;
7
8import java.awt.Color;
9import java.awt.Point;
10import java.awt.Polygon;
11import java.awt.geom.Point2D;
12import java.util.ArrayList;
13import java.util.Arrays;
14import java.util.Collection;
15import java.util.Collections;
16import java.util.Comparator;
17import java.util.LinkedList;
18import java.util.List;
19
20import org.openstreetmap.josm.Main;
21import org.openstreetmap.josm.data.Bounds;
22import org.openstreetmap.josm.data.coor.EastNorth;
23import org.openstreetmap.josm.data.coor.LatLon;
24import org.openstreetmap.josm.data.osm.BBox;
25import org.openstreetmap.josm.data.osm.DataSet;
26import org.openstreetmap.josm.data.osm.Node;
27import org.openstreetmap.josm.data.osm.OsmPrimitive;
28import org.openstreetmap.josm.data.osm.OsmUtils;
29import org.openstreetmap.josm.data.osm.Relation;
30import org.openstreetmap.josm.data.osm.RelationMember;
31import org.openstreetmap.josm.data.osm.Way;
32import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
33import org.openstreetmap.josm.gui.DefaultNameFormatter;
34import org.openstreetmap.josm.gui.mappaint.AreaElemStyle;
35import org.openstreetmap.josm.gui.mappaint.ElemStyle;
36import org.openstreetmap.josm.gui.mappaint.ElemStyles;
37import org.openstreetmap.josm.gui.mappaint.IconElemStyle;
38import org.openstreetmap.josm.gui.mappaint.LineElemStyle;
39import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
40import org.openstreetmap.josm.tools.LanguageInfo;
41
42public class MapPaintVisitor extends SimplePaintVisitor {
43 protected boolean useRealWidth;
44 protected boolean zoomLevelDisplay;
45 protected boolean drawMultipolygon;
46 protected boolean drawRestriction;
47 protected boolean leftHandTraffic;
48 protected int showNames;
49 protected int showIcons;
50 protected int useStrokes;
51 protected int fillAlpha;
52 protected Color untaggedColor;
53 protected Color textColor;
54 protected Color areaTextColor;
55 protected ElemStyles.StyleSet styles;
56 protected double circum;
57 protected double dist;
58 protected boolean useStyleCache;
59 private static int paintid = 0;
60 private EastNorth minEN;
61 private EastNorth maxEN;
62 private MapPainter painter;
63 protected Collection<String> regionalNameOrder;
64
65 protected boolean isZoomOk(ElemStyle e) {
66 if (!zoomLevelDisplay) /* show everything if the user wishes so */
67 return true;
68
69 if(e == null) /* the default for things that don't have a rule (show, if scale is smaller than 1500m) */
70 return (circum < 1500);
71
72 return !(circum >= e.maxScale || circum < e.minScale);
73 }
74
75 public ElemStyle getPrimitiveStyle(OsmPrimitive osm) {
76 if(!useStyleCache)
77 return ((styles != null) ? styles.get(osm) : null);
78
79 if(osm.mappaintStyle == null && styles != null) {
80 osm.mappaintStyle = styles.get(osm);
81 if(osm instanceof Way) {
82 ((Way)osm).isMappaintArea = styles.isArea(osm);
83 }
84 }
85 return osm.mappaintStyle;
86 }
87
88 public IconElemStyle getPrimitiveNodeStyle(OsmPrimitive osm) {
89 if(!useStyleCache)
90 return (styles != null) ? styles.getIcon(osm) : null;
91
92 if(osm.mappaintStyle == null && styles != null) {
93 osm.mappaintStyle = styles.getIcon(osm);
94 }
95
96 return (IconElemStyle)osm.mappaintStyle;
97 }
98
99 public boolean isPrimitiveArea(Way osm) {
100 if(!useStyleCache)
101 return styles.isArea(osm);
102
103 if(osm.mappaintStyle == null && styles != null) {
104 osm.mappaintStyle = styles.get(osm);
105 osm.isMappaintArea = styles.isArea(osm);
106 }
107 return osm.isMappaintArea;
108 }
109
110 /**
111 * Draw a small rectangle.
112 * White if selected (as always) or red otherwise.
113 *
114 * @param n The node to draw.
115 */
116 @Override
117 public void visit(Node n) {}
118 public void drawNode(Node n) {
119 /* check, if the node is visible at all */
120 if((n.getEastNorth().east() > maxEN.east() ) ||
121 (n.getEastNorth().north() > maxEN.north()) ||
122 (n.getEastNorth().east() < minEN.east() ) ||
123 (n.getEastNorth().north() < minEN.north()))
124 return;
125
126 IconElemStyle nodeStyle = (IconElemStyle)getPrimitiveStyle(n);
127
128 if (nodeStyle != null && isZoomOk(nodeStyle) && showIcons > dist) {
129 painter.drawNodeIcon(n, (inactive || n.isDisabled())?nodeStyle.getDisabledIcon():nodeStyle.icon,
130 nodeStyle.annotate, data.isSelected(n), getNodeName(n));
131 } else {
132 if (isZoomOk(null)) {
133 if (n.highlighted) {
134 painter.drawNode(n, highlightColor, selectedNodeSize, selectedNodeRadius, fillSelectedNode,
135 getNodeName(n));
136 } else if (data.isSelected(n)) {
137 painter.drawNode(n, selectedColor, selectedNodeSize, selectedNodeRadius, fillSelectedNode,
138 getNodeName(n));
139 } else if (n.isTagged()) {
140 painter.drawNode(n, nodeColor, taggedNodeSize, taggedNodeRadius, fillUnselectedNode,
141 getNodeName(n));
142 } else if (inactive || n.isDisabled()) {
143 painter.drawNode(n, inactiveColor, unselectedNodeSize, unselectedNodeRadius, fillUnselectedNode,
144 getNodeName(n));
145 } else {
146 painter.drawNode(n, nodeColor, unselectedNodeSize, unselectedNodeRadius, fillUnselectedNode,
147 getNodeName(n));
148 }
149 }
150 }
151 }
152
153 /**
154 * Draw a line for all segments, according to tags.
155 * @param w The way to draw.
156 */
157 @Override
158 public void visit(Way w) {}
159 public void drawWay(Way w, int fillAreas) {
160 if(w.getNodesCount() < 2)
161 return;
162
163 if (w.hasIncompleteNodes())
164 return;
165
166 /* check, if the way is visible at all */
167 double minx = 10000;
168 double maxx = -10000;
169 double miny = 10000;
170 double maxy = -10000;
171
172 for (Node n : w.getNodes())
173 {
174 if(n.getEastNorth().east() > maxx) {
175 maxx = n.getEastNorth().east();
176 }
177 if(n.getEastNorth().north() > maxy) {
178 maxy = n.getEastNorth().north();
179 }
180 if(n.getEastNorth().east() < minx) {
181 minx = n.getEastNorth().east();
182 }
183 if(n.getEastNorth().north() < miny) {
184 miny = n.getEastNorth().north();
185 }
186 }
187
188 if ((minx > maxEN.east()) ||
189 (miny > maxEN.north()) ||
190 (maxx < minEN.east()) ||
191 (maxy < minEN.north()))
192 return;
193
194 ElemStyle wayStyle = getPrimitiveStyle(w);
195
196 if(!isZoomOk(wayStyle))
197 return;
198
199 if(wayStyle==null)
200 {
201 /* way without style */
202 drawWay(w, null, untaggedColor, data.isSelected(w));
203 }
204 else if(wayStyle instanceof LineElemStyle)
205 {
206 /* way with line style */
207 drawWay(w, (LineElemStyle)wayStyle, untaggedColor, data.isSelected(w));
208 }
209 else if (wayStyle instanceof AreaElemStyle)
210 {
211 AreaElemStyle areaStyle = (AreaElemStyle) wayStyle;
212 /* way with area style */
213 if (fillAreas > dist)
214 {
215 painter.drawArea(getPolygon(w), (data.isSelected(w) ? selectedColor : areaStyle.color), getWayName(w));
216 if(!w.isClosed()) {
217 putError(w, tr("Area style way is not closed."), true);
218 }
219 }
220 drawWay(w, areaStyle.line, areaStyle.color, data.isSelected(w));
221 }
222 }
223
224 public void drawWay(Way w, LineElemStyle l, Color color, boolean selected) {
225 /* show direction arrows, if draw.segment.relevant_directions_only is not set,
226 the way is tagged with a direction key
227 (even if the tag is negated as in oneway=false) or the way is selected */
228 boolean showDirection = data.isSelected(w) || ((!useRealWidth) && (showDirectionArrow
229 && (!showRelevantDirectionsOnly || w.hasDirectionKeys())));
230 /* head only takes over control if the option is true,
231 the direction should be shown at all and not only because it's selected */
232 boolean showOnlyHeadArrowOnly = showDirection && !data.isSelected(w) && showHeadArrowOnly;
233 int width = defaultSegmentWidth;
234 int realWidth = 0; /* the real width of the element in meters */
235 float dashed[] = new float[0];
236 Color dashedColor = null;
237 Node lastN;
238
239 if(l != null) {
240 if (l.color != null) {
241 color = l.color;
242 }
243 width = l.width;
244 realWidth = l.realWidth;
245 dashed = l.getDashed();
246 dashedColor = l.dashedColor;
247 }
248 if(selected) {
249 color = selectedColor;
250 }
251 if (realWidth > 0 && useRealWidth && !showDirection) {
252 int tmpWidth = (int) (100 / (float) (circum / realWidth));
253 if (tmpWidth > width) {
254 width = tmpWidth;
255 }
256
257 /* if we have a "width" tag, try use it */
258 /* (this might be slow and could be improved by caching the value in the Way, on the other hand only used if "real width" is enabled) */
259 String widthTag = w.get("width");
260 if(widthTag == null) {
261 widthTag = w.get("est_width");
262 }
263 if(widthTag != null) {
264 try {
265 width = Integer.parseInt(widthTag);
266 }
267 catch(NumberFormatException nfe) {
268 }
269 }
270 }
271
272 if(w.highlighted) {
273 color = highlightColor;
274 } else if(data.isSelected(w)) {
275 color = selectedColor;
276 } else if(w.isDisabled()) {
277 color = inactiveColor;
278 }
279
280 /* draw overlays under the way */
281 if(l != null && l.overlays != null) {
282 for(LineElemStyle s : l.overlays) {
283 if(!s.over) {
284 painter.drawWay(w, s.color != null && !data.isSelected(w) ? s.color : color, s.getWidth(width),
285 s.getDashed(), s.dashedColor, false, false);
286 }
287 }
288 }
289
290 /* draw the way */
291 painter.drawWay(w, color, width, dashed, dashedColor, showDirection, showOnlyHeadArrowOnly);
292
293 /* draw overlays above the way */
294 if(l != null && l.overlays != null) {
295 for(LineElemStyle s : l.overlays) {
296 if(s.over) {
297 painter.drawWay(w, s.color != null && !data.isSelected(w) ? s.color : color, s.getWidth(width),
298 s.getDashed(), s.dashedColor, false, false);
299 }
300 }
301 }
302
303 if(showOrderNumber) {
304 int orderNumber = 0;
305 lastN = null;
306 for(Node n : w.getNodes()) {
307 if(lastN != null) {
308 orderNumber++;
309 drawOrderNumber(lastN, n, orderNumber);
310 }
311 lastN = n;
312 }
313 }
314 }
315
316 public Collection<PolyData> joinWays(Collection<Way> join, OsmPrimitive errs)
317 {
318 Collection<PolyData> res = new LinkedList<PolyData>();
319 Object[] joinArray = join.toArray();
320 int left = join.size();
321 while(left != 0)
322 {
323 Way w = null;
324 boolean selected = false;
325 List<Node> n = null;
326 boolean joined = true;
327 while(joined && left != 0)
328 {
329 joined = false;
330 for(int i = 0; i < joinArray.length && left != 0; ++i)
331 {
332 if(joinArray[i] != null)
333 {
334 Way c = (Way)joinArray[i];
335 if(w == null)
336 { w = c; selected = data.isSelected(w); joinArray[i] = null; --left; }
337 else
338 {
339 int mode = 0;
340 int cl = c.getNodesCount()-1;
341 int nl;
342 if(n == null)
343 {
344 nl = w.getNodesCount()-1;
345 if(w.getNode(nl) == c.getNode(0)) {
346 mode = 21;
347 } else if(w.getNode(nl) == c.getNode(cl)) {
348 mode = 22;
349 } else if(w.getNode(0) == c.getNode(0)) {
350 mode = 11;
351 } else if(w.getNode(0) == c.getNode(cl)) {
352 mode = 12;
353 }
354 }
355 else
356 {
357 nl = n.size()-1;
358 if(n.get(nl) == c.getNode(0)) {
359 mode = 21;
360 } else if(n.get(0) == c.getNode(cl)) {
361 mode = 12;
362 } else if(n.get(0) == c.getNode(0)) {
363 mode = 11;
364 } else if(n.get(nl) == c.getNode(cl)) {
365 mode = 22;
366 }
367 }
368 if(mode != 0)
369 {
370 joinArray[i] = null;
371 joined = true;
372 if(data.isSelected(c)) {
373 selected = true;
374 }
375 --left;
376 if(n == null) {
377 n = w.getNodes();
378 }
379 n.remove((mode == 21 || mode == 22) ? nl : 0);
380 if(mode == 21) {
381 n.addAll(c.getNodes());
382 } else if(mode == 12) {
383 n.addAll(0, c.getNodes());
384 } else if(mode == 22)
385 {
386 for(Node node : c.getNodes()) {
387 n.add(nl, node);
388 }
389 }
390 else /* mode == 11 */
391 {
392 for(Node node : c.getNodes()) {
393 n.add(0, node);
394 }
395 }
396 }
397 }
398 }
399 } /* for(i = ... */
400 } /* while(joined) */
401 if(n != null)
402 {
403 w = new Way(w);
404 w.setNodes(n);
405 }
406 if(!w.isClosed())
407 {
408 if(errs != null)
409 {
410 putError(errs, tr("multipolygon way ''{0}'' is not closed.",
411 w.getDisplayName(DefaultNameFormatter.getInstance())), true);
412 }
413 }
414 PolyData pd = new PolyData(w);
415 pd.selected = selected;
416 res.add(pd);
417 } /* while(left != 0) */
418
419 return res;
420 }
421
422 public void drawSelectedMember(OsmPrimitive osm, ElemStyle style, boolean area,
423 boolean areaselected)
424 {
425 if(osm instanceof Way)
426 {
427 if(style instanceof AreaElemStyle)
428 {
429 Way way = (Way)osm;
430 AreaElemStyle areaStyle = (AreaElemStyle)style;
431 drawWay(way, areaStyle.line, selectedColor, true);
432 if(area) {
433 painter.drawArea(getPolygon(way), (areaselected ? selectedColor : areaStyle.color), getWayName(way));
434 }
435 }
436 else
437 {
438 drawWay((Way)osm, (LineElemStyle)style, selectedColor, true);
439 }
440 }
441 else if(osm instanceof Node)
442 {
443 if(style != null && isZoomOk(style)) {
444 painter.drawNodeIcon((Node)osm, ((IconElemStyle)style).icon,
445 ((IconElemStyle)style).annotate, true, getNodeName((Node)osm));
446 } else if (isZoomOk(null)) {
447 painter.drawNode((Node)osm, selectedColor, selectedNodeSize, selectedNodeRadius, fillSelectedNode,
448 getNodeName((Node)osm));
449 }
450 }
451 osm.mappaintDrawnCode = paintid;
452 }
453
454 @Override
455 public void visit(Relation r) {}
456 public void paintUnselectedRelation(Relation r) {
457
458 if (drawMultipolygon && "multipolygon".equals(r.get("type")))
459 {
460 if(drawMultipolygon(r))
461 return;
462 }
463 else if (drawRestriction && "restriction".equals(r.get("type")))
464 {
465 drawRestriction(r);
466 }
467
468 if(data.isSelected(r)) /* draw ways*/
469 {
470 for (RelationMember m : r.getMembers())
471 {
472 if (m.isWay() && m.getMember().isDrawable())
473 {
474 drawSelectedMember(m.getMember(), styles != null ? getPrimitiveStyle(m.getMember())
475 : null, true, true);
476 }
477 }
478 }
479 }
480
481 public void drawRestriction(Relation r) {
482 Way fromWay = null;
483 Way toWay = null;
484 OsmPrimitive via = null;
485
486 /* find the "from", "via" and "to" elements */
487 for (RelationMember m : r.getMembers())
488 {
489 if (m.getMember().isDeleted()) {
490 putError(r, tr("Deleted member ''{0}'' in relation.",
491 m.getMember().getDisplayName(DefaultNameFormatter.getInstance())), true);
492 } else if(m.getMember().isIncomplete())
493 return;
494 else
495 {
496 if(m.isWay())
497 {
498 Way w = m.getWay();
499 if(w.getNodesCount() < 2)
500 {
501 putError(r, tr("Way ''{0}'' with less than two points.",
502 w.getDisplayName(DefaultNameFormatter.getInstance())), true);
503 }
504 else if("from".equals(m.getRole())) {
505 if(fromWay != null) {
506 putError(r, tr("More than one \"from\" way found."), true);
507 } else {
508 fromWay = w;
509 }
510 } else if("to".equals(m.getRole())) {
511 if(toWay != null) {
512 putError(r, tr("More than one \"to\" way found."), true);
513 } else {
514 toWay = w;
515 }
516 } else if("via".equals(m.getRole())) {
517 if(via != null) {
518 putError(r, tr("More than one \"via\" found."), true);
519 } else {
520 via = w;
521 }
522 } else {
523 putError(r, tr("Unknown role ''{0}''.", m.getRole()), true);
524 }
525 }
526 else if(m.isNode())
527 {
528 Node n = m.getNode();
529 if("via".equals(m.getRole()))
530 {
531 if(via != null) {
532 putError(r, tr("More than one \"via\" found."), true);
533 } else {
534 via = n;
535 }
536 } else {
537 putError(r, tr("Unknown role ''{0}''.", m.getRole()), true);
538 }
539 } else {
540 putError(r, tr("Unknown member type for ''{0}''.", m.getMember().getDisplayName(DefaultNameFormatter.getInstance())), true);
541 }
542 }
543 }
544
545 if (fromWay == null) {
546 putError(r, tr("No \"from\" way found."), true);
547 return;
548 }
549 if (toWay == null) {
550 putError(r, tr("No \"to\" way found."), true);
551 return;
552 }
553 if (via == null) {
554 putError(r, tr("No \"via\" node or way found."), true);
555 return;
556 }
557
558 Node viaNode;
559 if(via instanceof Node)
560 {
561 viaNode = (Node) via;
562 if(!fromWay.isFirstLastNode(viaNode)) {
563 putError(r, tr("The \"from\" way doesn't start or end at a \"via\" node."), true);
564 return;
565 }
566 if(!toWay.isFirstLastNode(viaNode)) {
567 putError(r, tr("The \"to\" way doesn't start or end at a \"via\" node."), true);
568 }
569 }
570 else
571 {
572 Way viaWay = (Way) via;
573 Node firstNode = viaWay.firstNode();
574 Node lastNode = viaWay.lastNode();
575 boolean onewayvia = false;
576
577 String onewayviastr = viaWay.get("oneway");
578 if(onewayviastr != null)
579 {
580 if("-1".equals(onewayviastr)) {
581 onewayvia = true;
582 Node tmp = firstNode;
583 firstNode = lastNode;
584 lastNode = tmp;
585 } else {
586 onewayvia = OsmUtils.getOsmBoolean(onewayviastr);
587 }
588 }
589
590 if(fromWay.isFirstLastNode(firstNode)) {
591 viaNode = firstNode;
592 } else if (!onewayvia && fromWay.isFirstLastNode(lastNode)) {
593 viaNode = lastNode;
594 } else {
595 putError(r, tr("The \"from\" way doesn't start or end at the \"via\" way."), true);
596 return;
597 }
598 if(!toWay.isFirstLastNode(viaNode == firstNode ? lastNode : firstNode)) {
599 putError(r, tr("The \"to\" way doesn't start or end at the \"via\" way."), true);
600 }
601 }
602
603 /* find the "direct" nodes before the via node */
604 Node fromNode = null;
605 if(fromWay.firstNode() == via) {
606 fromNode = fromWay.getNode(1);
607 } else {
608 fromNode = fromWay.getNode(fromWay.getNodesCount()-2);
609 }
610
611 Point pFrom = nc.getPoint(fromNode);
612 Point pVia = nc.getPoint(viaNode);
613
614 /* starting from via, go back the "from" way a few pixels
615 (calculate the vector vx/vy with the specified length and the direction
616 away from the "via" node along the first segment of the "from" way)
617 */
618 double distanceFromVia=14;
619 double dx = (pFrom.x >= pVia.x) ? (pFrom.x - pVia.x) : (pVia.x - pFrom.x);
620 double dy = (pFrom.y >= pVia.y) ? (pFrom.y - pVia.y) : (pVia.y - pFrom.y);
621
622 double fromAngle;
623 if(dx == 0.0) {
624 fromAngle = Math.PI/2;
625 } else {
626 fromAngle = Math.atan(dy / dx);
627 }
628 double fromAngleDeg = Math.toDegrees(fromAngle);
629
630 double vx = distanceFromVia * Math.cos(fromAngle);
631 double vy = distanceFromVia * Math.sin(fromAngle);
632
633 if(pFrom.x < pVia.x) {
634 vx = -vx;
635 }
636 if(pFrom.y < pVia.y) {
637 vy = -vy;
638 }
639
640 /* go a few pixels away from the way (in a right angle)
641 (calculate the vx2/vy2 vector with the specified length and the direction
642 90degrees away from the first segment of the "from" way)
643 */
644 double distanceFromWay=10;
645 double vx2 = 0;
646 double vy2 = 0;
647 double iconAngle = 0;
648
649 if(pFrom.x >= pVia.x && pFrom.y >= pVia.y) {
650 if(!leftHandTraffic) {
651 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg - 90));
652 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg - 90));
653 } else {
654 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 90));
655 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 90));
656 }
657 iconAngle = 270+fromAngleDeg;
658 }
659 if(pFrom.x < pVia.x && pFrom.y >= pVia.y) {
660 if(!leftHandTraffic) {
661 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg));
662 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg));
663 } else {
664 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 180));
665 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 180));
666 }
667 iconAngle = 90-fromAngleDeg;
668 }
669 if(pFrom.x < pVia.x && pFrom.y < pVia.y) {
670 if(!leftHandTraffic) {
671 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 90));
672 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 90));
673 } else {
674 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg - 90));
675 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg - 90));
676 }
677 iconAngle = 90+fromAngleDeg;
678 }
679 if(pFrom.x >= pVia.x && pFrom.y < pVia.y) {
680 if(!leftHandTraffic) {
681 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 180));
682 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 180));
683 } else {
684 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg));
685 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg));
686 }
687 iconAngle = 270-fromAngleDeg;
688 }
689
690 IconElemStyle nodeStyle = getPrimitiveNodeStyle(r);
691
692 if (nodeStyle == null) {
693 putError(r, tr("Style for restriction {0} not found.", r.get("restriction")), true);
694 return;
695 }
696
697 painter.drawRestriction(inactive || r.isDisabled() ? nodeStyle.getDisabledIcon() : nodeStyle.icon,
698 pVia, vx, vx2, vy, vy2, iconAngle, data.isSelected(r));
699 }
700
701 class PolyData {
702 public Polygon poly = new Polygon();
703 public Way way;
704 public boolean selected = false;
705 private Point p = null;
706 private Collection<Polygon> inner = null;
707 PolyData(Way w)
708 {
709 way = w;
710 for (Node n : w.getNodes())
711 {
712 p = nc.getPoint(n);
713 poly.addPoint(p.x,p.y);
714 }
715 }
716 public int contains(Polygon p)
717 {
718 int contains = p.npoints;
719 for(int i = 0; i < p.npoints; ++i)
720 {
721 if(poly.contains(p.xpoints[i],p.ypoints[i])) {
722 --contains;
723 }
724 }
725 if(contains == 0) return 1;
726 if(contains == p.npoints) return 0;
727 return 2;
728 }
729 public void addInner(Polygon p)
730 {
731 if(inner == null) {
732 inner = new ArrayList<Polygon>();
733 }
734 inner.add(p);
735 }
736 public boolean isClosed()
737 {
738 return (poly.npoints >= 3
739 && poly.xpoints[0] == poly.xpoints[poly.npoints-1]
740 && poly.ypoints[0] == poly.ypoints[poly.npoints-1]);
741 }
742 public Polygon get()
743 {
744 if(inner != null)
745 {
746 for (Polygon pp : inner)
747 {
748 for(int i = 0; i < pp.npoints; ++i) {
749 poly.addPoint(pp.xpoints[i],pp.ypoints[i]);
750 }
751 poly.addPoint(p.x,p.y);
752 }
753 inner = null;
754 }
755 return poly;
756 }
757 }
758 void addInnerToOuters(Relation r, boolean incomplete, PolyData pdInner, LinkedList<PolyData> outerPolygons)
759 {
760 Way wInner = pdInner.way;
761 if(wInner != null && !wInner.isClosed())
762 {
763 Point pInner = nc.getPoint(wInner.getNode(0));
764 pdInner.poly.addPoint(pInner.x,pInner.y);
765 }
766 PolyData o = null;
767 for (PolyData pdOuter : outerPolygons)
768 {
769 Integer c = pdOuter.contains(pdInner.poly);
770 if(c >= 1)
771 {
772 if(c > 1 && pdOuter.way != null && pdOuter.way.isClosed())
773 {
774 putError(r, tr("Intersection between ways ''{0}'' and ''{1}''.",
775 pdOuter.way.getDisplayName(DefaultNameFormatter.getInstance()), wInner.getDisplayName(DefaultNameFormatter.getInstance())), true);
776 }
777 if(o == null || o.contains(pdOuter.poly) > 0) {
778 o = pdOuter;
779 }
780 }
781 }
782 if(o == null)
783 {
784 if(!incomplete)
785 {
786 putError(r, tr("Inner way ''{0}'' is outside.",
787 wInner.getDisplayName(DefaultNameFormatter.getInstance())), true);
788 }
789 o = outerPolygons.get(0);
790 }
791 o.addInner(pdInner.poly);
792 }
793
794 public boolean drawMultipolygon(Relation r) {
795 Collection<Way> inner = new LinkedList<Way>();
796 Collection<Way> outer = new LinkedList<Way>();
797 Collection<Way> innerclosed = new LinkedList<Way>();
798 Collection<Way> outerclosed = new LinkedList<Way>();
799 boolean incomplete = false;
800 boolean drawn = false;
801
802 for (RelationMember m : r.getMembers())
803 {
804 if (m.getMember().isDeleted()) {
805 putError(r, tr("Deleted member ''{0}'' in relation.",
806 m.getMember().getDisplayName(DefaultNameFormatter.getInstance())), true);
807 } else if(m.getMember().isIncomplete()) {
808 incomplete = true;
809 } else {
810 if(m.isWay()) {
811 Way w = m.getWay();
812 if(w.getNodesCount() < 2) {
813 putError(r, tr("Way ''{0}'' with less than two points.",
814 w.getDisplayName(DefaultNameFormatter.getInstance())), true);
815 }
816 else if("inner".equals(m.getRole())) {
817 inner.add(w);
818 } else if("outer".equals(m.getRole())) {
819 outer.add(w);
820 } else {
821 putError(r, tr("No useful role ''{0}'' for Way ''{1}''.",
822 m.getRole(), w.getDisplayName(DefaultNameFormatter.getInstance())), true);
823 if(!m.hasRole()) {
824 outer.add(w);
825 } else if(data.isSelected(r)) {
826 drawSelectedMember(m.getMember(), styles != null
827 ? getPrimitiveStyle(m.getMember()) : null, true, true);
828 }
829 }
830 }
831 else
832 {
833 putError(r, tr("Non-Way ''{0}'' in multipolygon.",
834 m.getMember().getDisplayName(DefaultNameFormatter.getInstance())), true);
835 }
836 }
837 }
838
839 ElemStyle wayStyle = styles != null ? getPrimitiveStyle(r) : null;
840 if(styles != null && (wayStyle == null || !(wayStyle instanceof AreaElemStyle)))
841 {
842 for (Way w : outer)
843 {
844 if(wayStyle == null) {
845 wayStyle = styles.getArea(w);
846 }
847 }
848 r.mappaintStyle = wayStyle;
849 }
850
851 if(wayStyle != null && wayStyle instanceof AreaElemStyle)
852 {
853 boolean zoomok = isZoomOk(wayStyle);
854 boolean visible = false;
855 Collection<Way> outerjoin = new LinkedList<Way>();
856 Collection<Way> innerjoin = new LinkedList<Way>();
857
858 drawn = true;
859 for (Way w : outer)
860 {
861 if(w.isClosed()) {
862 outerclosed.add(w);
863 } else {
864 outerjoin.add(w);
865 }
866 }
867 for (Way w : inner)
868 {
869 if(w.isClosed()) {
870 innerclosed.add(w);
871 } else {
872 innerjoin.add(w);
873 }
874 }
875 if(outerclosed.size() == 0 && outerjoin.size() == 0)
876 {
877 putError(r, tr("No outer way for multipolygon ''{0}''.",
878 r.getDisplayName(DefaultNameFormatter.getInstance())), true);
879 visible = true; /* prevent killing remaining ways */
880 }
881 else if(zoomok)
882 {
883 LinkedList<PolyData> outerPoly = new LinkedList<PolyData>();
884 for (Way w : outerclosed) {
885 outerPoly.add(new PolyData(w));
886 }
887 outerPoly.addAll(joinWays(outerjoin, incomplete ? null : r));
888 for (Way wInner : innerclosed)
889 {
890 PolyData pdInner = new PolyData(wInner);
891 // incomplete is probably redundant
892 addInnerToOuters(r, incomplete, pdInner, outerPoly);
893 }
894 for (PolyData pdInner : joinWays(innerjoin, incomplete ? null : r)) {
895 addInnerToOuters(r, incomplete, pdInner, outerPoly);
896 }
897 AreaElemStyle areaStyle = (AreaElemStyle)wayStyle;
898 for (PolyData pd : outerPoly) {
899 Polygon p = pd.get();
900 if(!isPolygonVisible(p)) {
901 continue;
902 }
903
904 boolean selected = pd.selected || data.isSelected(pd.way) || data.isSelected(r);
905 painter.drawArea(p, selected ? selectedColor : areaStyle.color, null);
906 visible = true;
907 }
908 }
909 if(!visible)
910 return drawn;
911 for (Way wInner : inner)
912 {
913 ElemStyle innerStyle = getPrimitiveStyle(wInner);
914 if(innerStyle == null)
915 {
916 if (data.isSelected(wInner)) {
917 continue;
918 }
919 if(zoomok && (wInner.mappaintDrawnCode != paintid
920 || outer.size() == 0))
921 {
922 drawWay(wInner, ((AreaElemStyle)wayStyle).line,
923 ((AreaElemStyle)wayStyle).color, data.isSelected(wInner)
924 || data.isSelected(r));
925 }
926 wInner.mappaintDrawnCode = paintid;
927 }
928 else
929 {
930 if(data.isSelected(r))
931 {
932 drawSelectedMember(wInner, innerStyle,
933 !wayStyle.equals(innerStyle), data.isSelected(wInner));
934 }
935 if(wayStyle.equals(innerStyle))
936 {
937 putError(r, tr("Style for inner way ''{0}'' equals multipolygon.",
938 wInner.getDisplayName(DefaultNameFormatter.getInstance())), false);
939 if(!data.isSelected(r)) {
940 wInner.mappaintDrawnAreaCode = paintid;
941 }
942 }
943 }
944 }
945 for (Way wOuter : outer)
946 {
947 ElemStyle outerStyle = getPrimitiveStyle(wOuter);
948 if(outerStyle == null)
949 {
950 // Selected ways are drawn at the very end
951 if (data.isSelected(wOuter)) {
952 continue;
953 }
954 if(zoomok)
955 {
956 drawWay(wOuter, ((AreaElemStyle)wayStyle).line,
957 ((AreaElemStyle)wayStyle).color, data.isSelected(wOuter)
958 || data.isSelected(r));
959 }
960 wOuter.mappaintDrawnCode = paintid;
961 }
962 else
963 {
964 if(outerStyle instanceof AreaElemStyle
965 && !wayStyle.equals(outerStyle))
966 {
967 putError(r, tr("Style for outer way ''{0}'' mismatches.",
968 wOuter.getDisplayName(DefaultNameFormatter.getInstance())), true);
969 }
970 if(data.isSelected(r))
971 {
972 drawSelectedMember(wOuter, outerStyle, false, false);
973 }
974 else if(outerStyle instanceof AreaElemStyle) {
975 wOuter.mappaintDrawnAreaCode = paintid;
976 }
977 }
978 }
979 }
980 return drawn;
981 }
982
983 protected Polygon getPolygon(Way w)
984 {
985 Polygon polygon = new Polygon();
986
987 for (Node n : w.getNodes())
988 {
989 Point p = nc.getPoint(n);
990 polygon.addPoint(p.x,p.y);
991 }
992 return polygon;
993 }
994
995 protected Point2D getCentroid(Polygon p)
996 {
997 double cx = 0.0, cy = 0.0, a = 0.0;
998
999 // usually requires points[0] == points[npoints] and can then use i+1 instead of j.
1000 // Faked it slowly using j. If this is really gets used, this should be fixed.
1001 for (int i = 0; i < p.npoints; i++) {
1002 int j = i+1 == p.npoints ? 0 : i+1;
1003 a += (p.xpoints[i] * p.ypoints[j]) - (p.ypoints[i] * p.xpoints[j]);
1004 cx += (p.xpoints[i] + p.xpoints[j]) * (p.xpoints[i] * p.ypoints[j] - p.ypoints[i] * p.xpoints[j]);
1005 cy += (p.ypoints[i] + p.ypoints[j]) * (p.xpoints[i] * p.ypoints[j] - p.ypoints[i] * p.xpoints[j]);
1006 }
1007 return new Point2D.Double(cx / (3.0*a), cy / (3.0*a));
1008 }
1009
1010 protected double getArea(Polygon p)
1011 {
1012 double sum = 0.0;
1013
1014 // usually requires points[0] == points[npoints] and can then use i+1 instead of j.
1015 // Faked it slowly using j. If this is really gets used, this should be fixed.
1016 for (int i = 0; i < p.npoints; i++) {
1017 int j = i+1 == p.npoints ? 0 : i+1;
1018 sum = sum + (p.xpoints[i] * p.ypoints[j]) - (p.ypoints[i] * p.xpoints[j]);
1019 }
1020 return Math.abs(sum/2.0);
1021 }
1022
1023 protected String getNodeName(Node n) {
1024 if (showNames > dist) {
1025 String name = null;
1026 if (n.hasKeys()) {
1027 for (String rn : regionalNameOrder) {
1028 name = n.get(rn);
1029 if (name != null) {
1030 break;
1031 }
1032 }
1033 }
1034 return name;
1035 } else
1036 return null;
1037 }
1038
1039 protected String getWayName(Way w) {
1040 if (showNames > dist) {
1041 String name = null;
1042 if (w.hasKeys()) {
1043 for (String rn : regionalNameOrder) {
1044 name = w.get(rn);
1045 if (name != null) {
1046 break;
1047 }
1048 }
1049 }
1050 return name;
1051 } else
1052 return null;
1053 }
1054
1055 @Override
1056 public void getColors()
1057 {
1058 super.getColors();
1059 untaggedColor = PaintColors.UNTAGGED.get();
1060 textColor = PaintColors.TEXT.get();
1061 areaTextColor = PaintColors.AREA_TEXT.get();
1062 }
1063
1064 DataSet data;
1065
1066 <T extends OsmPrimitive> Collection<T> selectedLast(final DataSet data, Collection <T> prims) {
1067 ArrayList<T> sorted = new ArrayList<T>(prims);
1068 Collections.sort(sorted,
1069 new Comparator<T>() {
1070 public int compare(T o1, T o2) {
1071 boolean s1 = data.isSelected(o1);
1072 boolean s2 = data.isSelected(o2);
1073 if (s1 && !s2)
1074 return 1;
1075 if (!s1 && s2)
1076 return -1;
1077 return o1.compareTo(o2);
1078 }
1079 });
1080 return sorted;
1081 }
1082
1083 /* Shows areas before non-areas */
1084 @Override
1085 public void visitAll(DataSet data, boolean virtual, Bounds bounds) {
1086 BBox bbox = new BBox(bounds);
1087 this.data = data;
1088
1089 useStyleCache = Main.pref.getBoolean("mappaint.cache", true);
1090 int fillAreas = Main.pref.getInteger("mappaint.fillareas", 10000000);
1091 fillAlpha = Math.min(255, Math.max(0, Integer.valueOf(Main.pref.getInteger("mappaint.fillalpha", 50))));
1092 showNames = Main.pref.getInteger("mappaint.shownames", 10000000);
1093 showIcons = Main.pref.getInteger("mappaint.showicons", 10000000);
1094 useStrokes = Main.pref.getInteger("mappaint.strokes", 10000000);
1095 LatLon ll1 = nc.getLatLon(0, 0);
1096 LatLon ll2 = nc.getLatLon(100, 0);
1097 dist = ll1.greatCircleDistance(ll2);
1098
1099 getSettings(virtual);
1100 useRealWidth = Main.pref.getBoolean("mappaint.useRealWidth", false);
1101 zoomLevelDisplay = Main.pref.getBoolean("mappaint.zoomLevelDisplay", false);
1102 circum = Main.map.mapView.getDist100Pixel();
1103 styles = MapPaintStyles.getStyles().getStyleSet();
1104 drawMultipolygon = Main.pref.getBoolean("mappaint.multipolygon", true);
1105 drawRestriction = Main.pref.getBoolean("mappaint.restriction", true);
1106 leftHandTraffic = Main.pref.getBoolean("mappaint.lefthandtraffic", false);
1107 String[] names = {"name:" + LanguageInfo.getJOSMLocaleCode(), "name", "int_name", "ref", "operator", "brand", "addr:housenumber"};
1108 minEN = nc.getEastNorth(0, nc.getHeight() - 1);
1109 maxEN = nc.getEastNorth(nc.getWidth() - 1, 0);
1110 regionalNameOrder = Main.pref.getCollection("mappaint.nameOrder", Arrays.asList(names));
1111
1112 this.painter = new MapPainter(g, inactive, nc, useStrokes > dist);
1113
1114 data.clearErrors();
1115
1116 ++paintid;
1117
1118 if (fillAreas > dist && styles != null && styles.hasAreas()) {
1119 Collection<Way> noAreaWays = new LinkedList<Way>();
1120
1121 /*** RELATIONS ***/
1122 for (final Relation osm: data.getRelations()) {
1123 if (osm.isDrawable()) {
1124 paintUnselectedRelation(osm);
1125 }
1126 }
1127
1128 /*** AREAS ***/
1129 for (final Way osm : selectedLast(data, data.searchWays(bbox))) {
1130 if (osm.isDrawable() && osm.mappaintDrawnCode != paintid) {
1131 if (isPrimitiveArea(osm) && osm.mappaintDrawnAreaCode != paintid) {
1132 drawWay(osm, fillAreas);
1133 } else {
1134 noAreaWays.add(osm);
1135 }
1136 }
1137 }
1138
1139 /*** WAYS ***/
1140 for (final Way osm : noAreaWays) {
1141 drawWay(osm, 0);
1142 }
1143 } else {
1144 drawMultipolygon = false;
1145
1146 /*** RELATIONS ***/
1147 for (final Relation osm: data.getRelations()) {
1148 if (osm.isDrawable()) {
1149 paintUnselectedRelation(osm);
1150 }
1151 }
1152
1153 /*** WAYS (filling disabled) ***/
1154 for (final Way way: data.getWays()) {
1155 if (way.isDrawable() && !data.isSelected(way)) {
1156 drawWay(way, 0);
1157 }
1158 }
1159 }
1160
1161 /*** SELECTED ***/
1162 for (final OsmPrimitive osm : data.getSelected()) {
1163 if (osm.isUsable() && !(osm instanceof Node) && osm.mappaintDrawnCode != paintid) {
1164 osm.visit(new AbstractVisitor() {
1165 public void visit(Way w) {
1166 drawWay(w, 0);
1167 }
1168
1169 public void visit(Node n) {
1170 // Selected nodes are painted in following part
1171 }
1172
1173 public void visit(Relation r) {
1174 /* TODO: is it possible to do this like the nodes/ways code? */
1175 // Only nodes are painted, ways was already painted before (this might cause that
1176 // way in selected relation is hidden by another way)
1177 for (RelationMember m : r.getMembers()) {
1178 if (m.isNode() && m.getMember().isDrawable()) {
1179 drawSelectedMember(m.getMember(), styles != null ? getPrimitiveStyle(m.getMember()) : null, true, true);
1180 }
1181 }
1182 }
1183 });
1184 }
1185 }
1186
1187 /*** NODES ***/
1188 for (final Node osm: data.searchNodes(bbox)) {
1189 if (!osm.isIncomplete() && !osm.isDeleted() && (data.isSelected(osm) || !osm.isFiltered())
1190 && osm.mappaintDrawnCode != paintid) {
1191 drawNode(osm);
1192 }
1193 }
1194
1195 drawVirtualNodes(data.searchWays(bbox));
1196 }
1197
1198 /**
1199 * Draw a number of the order of the two consecutive nodes within the
1200 * parents way
1201 */
1202 protected void drawOrderNumber(Node n1, Node n2, int orderNumber) {
1203 Point p1 = nc.getPoint(n1);
1204 Point p2 = nc.getPoint(n2);
1205 drawOrderNumber(p1, p2, orderNumber);
1206 }
1207
1208 public void putError(OsmPrimitive p, String text, boolean isError)
1209 {
1210 data.addError(p, isError ? tr("Error: {0}", text) : tr("Warning: {0}", text));
1211 }
1212}
Note: See TracBrowser for help on using the repository browser.