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

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

Make MapPaintVisitor independent of SimplePaintVisitor

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