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

Last change on this file since 2722 was 2706, checked in by stoecker, 14 years ago

fix #4234 - fix anti-aliasing - patch by Upliner

  • Property svn:eol-style set to native
File size: 38.8 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.Graphics2D;
9import java.awt.Point;
10import java.awt.Polygon;
11import java.awt.Rectangle;
12import java.awt.RenderingHints;
13import java.awt.geom.Point2D;
14import java.util.ArrayList;
15import java.util.Collection;
16import java.util.Collections;
17import java.util.Comparator;
18import java.util.LinkedList;
19import java.util.List;
20
21import org.openstreetmap.josm.Main;
22import org.openstreetmap.josm.data.Bounds;
23import org.openstreetmap.josm.data.coor.EastNorth;
24import org.openstreetmap.josm.data.coor.LatLon;
25import org.openstreetmap.josm.data.osm.BBox;
26import org.openstreetmap.josm.data.osm.DataSet;
27import org.openstreetmap.josm.data.osm.Node;
28import org.openstreetmap.josm.data.osm.OsmPrimitive;
29import org.openstreetmap.josm.data.osm.OsmUtils;
30import org.openstreetmap.josm.data.osm.Relation;
31import org.openstreetmap.josm.data.osm.RelationMember;
32import org.openstreetmap.josm.data.osm.Way;
33import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
34import org.openstreetmap.josm.gui.DefaultNameFormatter;
35import org.openstreetmap.josm.gui.NavigatableComponent;
36import org.openstreetmap.josm.gui.mappaint.AreaElemStyle;
37import org.openstreetmap.josm.gui.mappaint.ElemStyle;
38import org.openstreetmap.josm.gui.mappaint.ElemStyles;
39import org.openstreetmap.josm.gui.mappaint.IconElemStyle;
40import org.openstreetmap.josm.gui.mappaint.LineElemStyle;
41import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
42import org.openstreetmap.josm.gui.mappaint.SimpleNodeElemStyle;
43
44public class MapPaintVisitor implements PaintVisitor {
45
46 private Graphics2D g;
47 private NavigatableComponent nc;
48
49 private boolean zoomLevelDisplay;
50 private boolean drawMultipolygon;
51 private boolean drawRestriction;
52 private boolean leftHandTraffic;
53 private ElemStyles.StyleSet styles;
54 private double circum;
55 private double dist;
56 private boolean useStyleCache;
57 private static int paintid = 0;
58 private EastNorth minEN;
59 private EastNorth maxEN;
60 private MapPainter painter;
61 private MapPaintSettings paintSettings;
62
63 private boolean inactive;
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
86 if (osm.mappaintStyle == null && osm instanceof Node) {
87 osm.mappaintStyle = SimpleNodeElemStyle.INSTANCE;
88 }
89
90 if (osm.mappaintStyle == null && osm instanceof Way) {
91 osm.mappaintStyle = LineElemStyle.UNTAGGED_WAY;
92 }
93
94 return osm.mappaintStyle;
95 }
96
97 public IconElemStyle getPrimitiveNodeStyle(OsmPrimitive osm) {
98 if(!useStyleCache)
99 return (styles != null) ? styles.getIcon(osm) : null;
100
101 if(osm.mappaintStyle == null && styles != null) {
102 osm.mappaintStyle = styles.getIcon(osm);
103 }
104
105 return (IconElemStyle)osm.mappaintStyle;
106 }
107
108 public boolean isPrimitiveArea(Way osm) {
109 if(!useStyleCache)
110 return styles.isArea(osm);
111
112 if(osm.mappaintStyle == null && styles != null) {
113 osm.mappaintStyle = styles.get(osm);
114 osm.isMappaintArea = styles.isArea(osm);
115 }
116 return osm.isMappaintArea;
117 }
118
119 public void drawNode(Node n) {
120 /* check, if the node is visible at all */
121 if((n.getEastNorth().east() > maxEN.east() ) ||
122 (n.getEastNorth().north() > maxEN.north()) ||
123 (n.getEastNorth().east() < minEN.east() ) ||
124 (n.getEastNorth().north() < minEN.north()))
125 return;
126
127 ElemStyle nodeStyle = getPrimitiveStyle(n);
128
129 if (isZoomOk(nodeStyle)) {
130 nodeStyle.paintPrimitive(n, paintSettings, painter, n.isSelected());
131 }
132 }
133
134 public void drawWay(Way w, int fillAreas) {
135 if(w.getNodesCount() < 2)
136 return;
137
138 if (w.hasIncompleteNodes())
139 return;
140
141 /* check, if the way is visible at all */
142 double minx = 10000;
143 double maxx = -10000;
144 double miny = 10000;
145 double maxy = -10000;
146
147 for (Node n : w.getNodes())
148 {
149 if(n.getEastNorth().east() > maxx) {
150 maxx = n.getEastNorth().east();
151 }
152 if(n.getEastNorth().north() > maxy) {
153 maxy = n.getEastNorth().north();
154 }
155 if(n.getEastNorth().east() < minx) {
156 minx = n.getEastNorth().east();
157 }
158 if(n.getEastNorth().north() < miny) {
159 miny = n.getEastNorth().north();
160 }
161 }
162
163 if ((minx > maxEN.east()) ||
164 (miny > maxEN.north()) ||
165 (maxx < minEN.east()) ||
166 (maxy < minEN.north()))
167 return;
168
169 ElemStyle wayStyle = getPrimitiveStyle(w);
170
171 if(!isZoomOk(wayStyle))
172 return;
173
174 if (wayStyle == null) {
175 wayStyle = LineElemStyle.UNTAGGED_WAY;
176 }
177
178 if(wayStyle instanceof LineElemStyle) {
179 wayStyle.paintPrimitive(w, paintSettings, painter, data.isSelected(w));
180 } else if (wayStyle instanceof AreaElemStyle) {
181 AreaElemStyle areaStyle = (AreaElemStyle) wayStyle;
182 /* way with area style */
183 if (fillAreas > dist)
184 {
185 painter.drawArea(getPolygon(w), (data.isSelected(w) ? paintSettings.getSelectedColor() : areaStyle.color), painter.getWayName(w));
186 if(!w.isClosed()) {
187 putError(w, tr("Area style way is not closed."), true);
188 }
189 }
190 areaStyle.getLineStyle().paintPrimitive(w, paintSettings, painter, data.isSelected(w));
191 }
192 }
193
194 public Collection<PolyData> joinWays(Collection<Way> join, OsmPrimitive errs)
195 {
196 Collection<PolyData> res = new LinkedList<PolyData>();
197 Way[] joinArray = join.toArray(new Way[join.size()]);
198 int left = join.size();
199 while(left != 0)
200 {
201 Way w = null;
202 boolean selected = false;
203 List<Node> n = null;
204 boolean joined = true;
205 while(joined && left != 0)
206 {
207 joined = false;
208 for(int i = 0; i < joinArray.length && left != 0; ++i)
209 {
210 if(joinArray[i] != null)
211 {
212 Way c = joinArray[i];
213 if(w == null)
214 { w = c; selected = data.isSelected(w); joinArray[i] = null; --left; }
215 else
216 {
217 int mode = 0;
218 int cl = c.getNodesCount()-1;
219 int nl;
220 if(n == null)
221 {
222 nl = w.getNodesCount()-1;
223 if(w.getNode(nl) == c.getNode(0)) {
224 mode = 21;
225 } else if(w.getNode(nl) == c.getNode(cl)) {
226 mode = 22;
227 } else if(w.getNode(0) == c.getNode(0)) {
228 mode = 11;
229 } else if(w.getNode(0) == c.getNode(cl)) {
230 mode = 12;
231 }
232 }
233 else
234 {
235 nl = n.size()-1;
236 if(n.get(nl) == c.getNode(0)) {
237 mode = 21;
238 } else if(n.get(0) == c.getNode(cl)) {
239 mode = 12;
240 } else if(n.get(0) == c.getNode(0)) {
241 mode = 11;
242 } else if(n.get(nl) == c.getNode(cl)) {
243 mode = 22;
244 }
245 }
246 if(mode != 0)
247 {
248 joinArray[i] = null;
249 joined = true;
250 if(data.isSelected(c)) {
251 selected = true;
252 }
253 --left;
254 if(n == null) {
255 n = w.getNodes();
256 }
257 n.remove((mode == 21 || mode == 22) ? nl : 0);
258 if(mode == 21) {
259 n.addAll(c.getNodes());
260 } else if(mode == 12) {
261 n.addAll(0, c.getNodes());
262 } else if(mode == 22)
263 {
264 for(Node node : c.getNodes()) {
265 n.add(nl, node);
266 }
267 }
268 else /* mode == 11 */
269 {
270 for(Node node : c.getNodes()) {
271 n.add(0, node);
272 }
273 }
274 }
275 }
276 }
277 } /* for(i = ... */
278 } /* while(joined) */
279 if(n != null)
280 {
281 w = new Way(w);
282 w.setNodes(n);
283 }
284 if(!w.isClosed())
285 {
286 if(errs != null)
287 {
288 putError(errs, tr("multipolygon way ''{0}'' is not closed.",
289 w.getDisplayName(DefaultNameFormatter.getInstance())), true);
290 }
291 }
292 PolyData pd = new PolyData(w);
293 pd.selected = selected;
294 res.add(pd);
295 } /* while(left != 0) */
296
297 return res;
298 }
299
300 public void drawSelectedMember(OsmPrimitive osm, ElemStyle style, boolean area,
301 boolean areaselected)
302 {
303 if(osm instanceof Way)
304 {
305 if(style instanceof AreaElemStyle) {
306 Way way = (Way)osm;
307 AreaElemStyle areaStyle = (AreaElemStyle)style;
308 areaStyle.getLineStyle().paintPrimitive(way, paintSettings, painter, true);
309 if(area) {
310 painter.drawArea(getPolygon(way), (areaselected ? paintSettings.getSelectedColor() : areaStyle.color), painter.getWayName(way));
311 }
312 } else {
313 style.paintPrimitive(osm, paintSettings, painter, true);
314 }
315 }
316 else if(osm instanceof Node)
317 {
318 if(isZoomOk(style)) {
319 style.paintPrimitive(osm, paintSettings, painter, true);
320 }
321 }
322 osm.mappaintDrawnCode = paintid;
323 }
324
325 public void paintUnselectedRelation(Relation r) {
326
327 if (drawMultipolygon && "multipolygon".equals(r.get("type")))
328 {
329 if(drawMultipolygon(r))
330 return;
331 }
332 else if (drawRestriction && "restriction".equals(r.get("type")))
333 {
334 drawRestriction(r);
335 }
336
337 if(data.isSelected(r)) /* draw ways*/
338 {
339 for (RelationMember m : r.getMembers())
340 {
341 if (m.isWay() && m.getMember().isDrawable())
342 {
343 drawSelectedMember(m.getMember(), styles != null ? getPrimitiveStyle(m.getMember())
344 : null, true, true);
345 }
346 }
347 }
348 }
349
350 public void drawRestriction(Relation r) {
351 Way fromWay = null;
352 Way toWay = null;
353 OsmPrimitive via = null;
354
355 /* find the "from", "via" and "to" elements */
356 for (RelationMember m : r.getMembers())
357 {
358 if (m.getMember().isDeleted()) {
359 putError(r, tr("Deleted member ''{0}'' in relation.",
360 m.getMember().getDisplayName(DefaultNameFormatter.getInstance())), true);
361 } else if(m.getMember().isIncomplete())
362 return;
363 else
364 {
365 if(m.isWay())
366 {
367 Way w = m.getWay();
368 if(w.getNodesCount() < 2)
369 {
370 putError(r, tr("Way ''{0}'' with less than two points.",
371 w.getDisplayName(DefaultNameFormatter.getInstance())), true);
372 }
373 else if("from".equals(m.getRole())) {
374 if(fromWay != null) {
375 putError(r, tr("More than one \"from\" way found."), true);
376 } else {
377 fromWay = w;
378 }
379 } else if("to".equals(m.getRole())) {
380 if(toWay != null) {
381 putError(r, tr("More than one \"to\" way found."), true);
382 } else {
383 toWay = w;
384 }
385 } else if("via".equals(m.getRole())) {
386 if(via != null) {
387 putError(r, tr("More than one \"via\" found."), true);
388 } else {
389 via = w;
390 }
391 } else {
392 putError(r, tr("Unknown role ''{0}''.", m.getRole()), true);
393 }
394 }
395 else if(m.isNode())
396 {
397 Node n = m.getNode();
398 if("via".equals(m.getRole()))
399 {
400 if(via != null) {
401 putError(r, tr("More than one \"via\" found."), true);
402 } else {
403 via = n;
404 }
405 } else {
406 putError(r, tr("Unknown role ''{0}''.", m.getRole()), true);
407 }
408 } else {
409 putError(r, tr("Unknown member type for ''{0}''.", m.getMember().getDisplayName(DefaultNameFormatter.getInstance())), true);
410 }
411 }
412 }
413
414 if (fromWay == null) {
415 putError(r, tr("No \"from\" way found."), true);
416 return;
417 }
418 if (toWay == null) {
419 putError(r, tr("No \"to\" way found."), true);
420 return;
421 }
422 if (via == null) {
423 putError(r, tr("No \"via\" node or way found."), true);
424 return;
425 }
426
427 Node viaNode;
428 if(via instanceof Node)
429 {
430 viaNode = (Node) via;
431 if(!fromWay.isFirstLastNode(viaNode)) {
432 putError(r, tr("The \"from\" way doesn't start or end at a \"via\" node."), true);
433 return;
434 }
435 if(!toWay.isFirstLastNode(viaNode)) {
436 putError(r, tr("The \"to\" way doesn't start or end at a \"via\" node."), true);
437 }
438 }
439 else
440 {
441 Way viaWay = (Way) via;
442 Node firstNode = viaWay.firstNode();
443 Node lastNode = viaWay.lastNode();
444 boolean onewayvia = false;
445
446 String onewayviastr = viaWay.get("oneway");
447 if(onewayviastr != null)
448 {
449 if("-1".equals(onewayviastr)) {
450 onewayvia = true;
451 Node tmp = firstNode;
452 firstNode = lastNode;
453 lastNode = tmp;
454 } else {
455 onewayvia = OsmUtils.getOsmBoolean(onewayviastr);
456 }
457 }
458
459 if(fromWay.isFirstLastNode(firstNode)) {
460 viaNode = firstNode;
461 } else if (!onewayvia && fromWay.isFirstLastNode(lastNode)) {
462 viaNode = lastNode;
463 } else {
464 putError(r, tr("The \"from\" way doesn't start or end at the \"via\" way."), true);
465 return;
466 }
467 if(!toWay.isFirstLastNode(viaNode == firstNode ? lastNode : firstNode)) {
468 putError(r, tr("The \"to\" way doesn't start or end at the \"via\" way."), true);
469 }
470 }
471
472 /* find the "direct" nodes before the via node */
473 Node fromNode = null;
474 if(fromWay.firstNode() == via) {
475 fromNode = fromWay.getNode(1);
476 } else {
477 fromNode = fromWay.getNode(fromWay.getNodesCount()-2);
478 }
479
480 Point pFrom = nc.getPoint(fromNode);
481 Point pVia = nc.getPoint(viaNode);
482
483 /* starting from via, go back the "from" way a few pixels
484 (calculate the vector vx/vy with the specified length and the direction
485 away from the "via" node along the first segment of the "from" way)
486 */
487 double distanceFromVia=14;
488 double dx = (pFrom.x >= pVia.x) ? (pFrom.x - pVia.x) : (pVia.x - pFrom.x);
489 double dy = (pFrom.y >= pVia.y) ? (pFrom.y - pVia.y) : (pVia.y - pFrom.y);
490
491 double fromAngle;
492 if(dx == 0.0) {
493 fromAngle = Math.PI/2;
494 } else {
495 fromAngle = Math.atan(dy / dx);
496 }
497 double fromAngleDeg = Math.toDegrees(fromAngle);
498
499 double vx = distanceFromVia * Math.cos(fromAngle);
500 double vy = distanceFromVia * Math.sin(fromAngle);
501
502 if(pFrom.x < pVia.x) {
503 vx = -vx;
504 }
505 if(pFrom.y < pVia.y) {
506 vy = -vy;
507 }
508
509 /* go a few pixels away from the way (in a right angle)
510 (calculate the vx2/vy2 vector with the specified length and the direction
511 90degrees away from the first segment of the "from" way)
512 */
513 double distanceFromWay=10;
514 double vx2 = 0;
515 double vy2 = 0;
516 double iconAngle = 0;
517
518 if(pFrom.x >= pVia.x && pFrom.y >= pVia.y) {
519 if(!leftHandTraffic) {
520 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg - 90));
521 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg - 90));
522 } else {
523 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 90));
524 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 90));
525 }
526 iconAngle = 270+fromAngleDeg;
527 }
528 if(pFrom.x < pVia.x && pFrom.y >= pVia.y) {
529 if(!leftHandTraffic) {
530 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg));
531 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg));
532 } else {
533 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 180));
534 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 180));
535 }
536 iconAngle = 90-fromAngleDeg;
537 }
538 if(pFrom.x < pVia.x && pFrom.y < pVia.y) {
539 if(!leftHandTraffic) {
540 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 90));
541 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 90));
542 } else {
543 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg - 90));
544 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg - 90));
545 }
546 iconAngle = 90+fromAngleDeg;
547 }
548 if(pFrom.x >= pVia.x && pFrom.y < pVia.y) {
549 if(!leftHandTraffic) {
550 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 180));
551 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 180));
552 } else {
553 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg));
554 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg));
555 }
556 iconAngle = 270-fromAngleDeg;
557 }
558
559 IconElemStyle nodeStyle = getPrimitiveNodeStyle(r);
560
561 if (nodeStyle == null) {
562 putError(r, tr("Style for restriction {0} not found.", r.get("restriction")), true);
563 return;
564 }
565
566 painter.drawRestriction(inactive || r.isDisabled() ? nodeStyle.getDisabledIcon() : nodeStyle.icon,
567 pVia, vx, vx2, vy, vy2, iconAngle, data.isSelected(r));
568 }
569
570 class PolyData {
571 public Polygon poly = new Polygon();
572 public Way way;
573 public boolean selected = false;
574 private Point p = null;
575 private Collection<Polygon> inner = null;
576 PolyData(Way w)
577 {
578 way = w;
579 for (Node n : w.getNodes())
580 {
581 p = nc.getPoint(n);
582 poly.addPoint(p.x,p.y);
583 }
584 }
585 public int contains(Polygon p)
586 {
587 int contains = p.npoints;
588 for(int i = 0; i < p.npoints; ++i)
589 {
590 if(poly.contains(p.xpoints[i],p.ypoints[i])) {
591 --contains;
592 }
593 }
594 if(contains == 0) return 1;
595 if(contains == p.npoints) return 0;
596 return 2;
597 }
598 public void addInner(Polygon p)
599 {
600 if(inner == null) {
601 inner = new ArrayList<Polygon>();
602 }
603 inner.add(p);
604 }
605 public boolean isClosed()
606 {
607 return (poly.npoints >= 3
608 && poly.xpoints[0] == poly.xpoints[poly.npoints-1]
609 && poly.ypoints[0] == poly.ypoints[poly.npoints-1]);
610 }
611 public Polygon get()
612 {
613 if(inner != null)
614 {
615 for (Polygon pp : inner)
616 {
617 for(int i = 0; i < pp.npoints; ++i) {
618 poly.addPoint(pp.xpoints[i],pp.ypoints[i]);
619 }
620 poly.addPoint(p.x,p.y);
621 }
622 inner = null;
623 }
624 return poly;
625 }
626 }
627 void addInnerToOuters(Relation r, boolean incomplete, PolyData pdInner, LinkedList<PolyData> outerPolygons)
628 {
629 Way wInner = pdInner.way;
630 if(wInner != null && !wInner.isClosed())
631 {
632 Point pInner = nc.getPoint(wInner.getNode(0));
633 pdInner.poly.addPoint(pInner.x,pInner.y);
634 }
635 PolyData o = null;
636 for (PolyData pdOuter : outerPolygons)
637 {
638 int c = pdOuter.contains(pdInner.poly);
639 if(c >= 1)
640 {
641 if(c > 1 && pdOuter.way != null && pdOuter.way.isClosed())
642 {
643 putError(r, tr("Intersection between ways ''{0}'' and ''{1}''.",
644 pdOuter.way.getDisplayName(DefaultNameFormatter.getInstance()), wInner.getDisplayName(DefaultNameFormatter.getInstance())), true);
645 }
646 if(o == null || o.contains(pdOuter.poly) > 0) {
647 o = pdOuter;
648 }
649 }
650 }
651 if(o == null)
652 {
653 if(!incomplete)
654 {
655 putError(r, tr("Inner way ''{0}'' is outside.",
656 wInner.getDisplayName(DefaultNameFormatter.getInstance())), true);
657 }
658 o = outerPolygons.get(0);
659 }
660 o.addInner(pdInner.poly);
661 }
662
663 public boolean drawMultipolygon(Relation r) {
664 Collection<Way> inner = new LinkedList<Way>();
665 Collection<Way> outer = new LinkedList<Way>();
666 Collection<Way> innerclosed = new LinkedList<Way>();
667 Collection<Way> outerclosed = new LinkedList<Way>();
668 boolean incomplete = false;
669 boolean drawn = false;
670
671 // Fill inner and outer list with valid ways
672 for (RelationMember m : r.getMembers()) {
673 if (m.getMember().isDeleted()) {
674 putError(r, tr("Deleted member ''{0}'' in relation.",
675 m.getMember().getDisplayName(DefaultNameFormatter.getInstance())), true);
676 } else if (m.getMember().isIncomplete()) {
677 incomplete = true;
678 } else if(m.getMember().isDrawable()) {
679 if(m.isWay()) {
680 Way w = m.getWay();
681 if(w.getNodesCount() < 2) {
682 putError(r, tr("Way ''{0}'' with less than two points.",
683 w.getDisplayName(DefaultNameFormatter.getInstance())), true);
684 }
685 else if("inner".equals(m.getRole())) {
686 inner.add(w);
687 } else if("outer".equals(m.getRole())) {
688 outer.add(w);
689 } else {
690 putError(r, tr("No useful role ''{0}'' for Way ''{1}''.",
691 m.getRole(), w.getDisplayName(DefaultNameFormatter.getInstance())), true);
692 if(!m.hasRole()) {
693 outer.add(w);
694 } else if(data.isSelected(r)) {
695 // TODO Is this necessary?
696 drawSelectedMember(m.getMember(), styles != null
697 ? getPrimitiveStyle(m.getMember()) : null, true, true);
698 }
699 }
700 }
701 else
702 {
703 putError(r, tr("Non-Way ''{0}'' in multipolygon.",
704 m.getMember().getDisplayName(DefaultNameFormatter.getInstance())), true);
705 }
706 }
707 }
708
709 ElemStyle wayStyle = getPrimitiveStyle(r);
710
711 // If area style was not found for relation then use style of ways
712 if(styles != null && !(wayStyle instanceof AreaElemStyle)) {
713 for (Way w : outer) {
714 wayStyle = styles.getArea(w);
715 if(wayStyle != null) {
716 break;
717 }
718 }
719 r.mappaintStyle = wayStyle;
720 }
721
722 if (wayStyle instanceof AreaElemStyle) {
723 boolean zoomok = isZoomOk(wayStyle);
724 boolean visible = false;
725 Collection<Way> outerjoin = new LinkedList<Way>();
726 Collection<Way> innerjoin = new LinkedList<Way>();
727
728 drawn = true;
729 for (Way w : outer)
730 {
731 if(w.isClosed()) {
732 outerclosed.add(w);
733 } else {
734 outerjoin.add(w);
735 }
736 }
737 for (Way w : inner)
738 {
739 if(w.isClosed()) {
740 innerclosed.add(w);
741 } else {
742 innerjoin.add(w);
743 }
744 }
745
746
747 if(outerclosed.size() == 0 && outerjoin.size() == 0)
748 {
749 putError(r, tr("No outer way for multipolygon ''{0}''.",
750 r.getDisplayName(DefaultNameFormatter.getInstance())), true);
751 visible = true; /* prevent killing remaining ways */
752 }
753 else if(zoomok)
754 {
755 LinkedList<PolyData> outerPoly = new LinkedList<PolyData>();
756 for (Way w : outerclosed) {
757 outerPoly.add(new PolyData(w));
758 }
759 outerPoly.addAll(joinWays(outerjoin, incomplete ? null : r));
760 for (Way wInner : innerclosed)
761 {
762 PolyData pdInner = new PolyData(wInner);
763 // incomplete is probably redundant
764 addInnerToOuters(r, incomplete, pdInner, outerPoly);
765 }
766 for (PolyData pdInner : joinWays(innerjoin, incomplete ? null : r)) {
767 addInnerToOuters(r, incomplete, pdInner, outerPoly);
768 }
769 AreaElemStyle areaStyle = (AreaElemStyle)wayStyle;
770 for (PolyData pd : outerPoly) {
771 Polygon p = pd.get();
772 if(!isPolygonVisible(p)) {
773 continue;
774 }
775
776 boolean selected = pd.selected || data.isSelected(pd.way) || data.isSelected(r);
777 painter.drawArea(p, selected ? paintSettings.getSelectedColor() : areaStyle.color, null);
778 visible = true;
779 }
780 }
781
782 if(!visible)
783 return drawn;
784 for (Way wInner : inner)
785 {
786 ElemStyle innerStyle = getPrimitiveStyle(wInner);
787 if(innerStyle == null)
788 {
789 if (data.isSelected(wInner)) {
790 continue;
791 }
792 if(zoomok && (wInner.mappaintDrawnCode != paintid
793 || outer.size() == 0))
794 {
795 ((AreaElemStyle)wayStyle).getLineStyle().paintPrimitive(wInner, paintSettings, painter, (data.isSelected(wInner)
796 || data.isSelected(r)));
797 }
798 wInner.mappaintDrawnCode = paintid;
799 }
800 else
801 {
802 if(data.isSelected(r))
803 {
804 drawSelectedMember(wInner, innerStyle,
805 !wayStyle.equals(innerStyle), data.isSelected(wInner));
806 }
807 if(wayStyle.equals(innerStyle))
808 {
809 putError(r, tr("Style for inner way ''{0}'' equals multipolygon.",
810 wInner.getDisplayName(DefaultNameFormatter.getInstance())), false);
811 if(!data.isSelected(r)) {
812 wInner.mappaintDrawnAreaCode = paintid;
813 }
814 }
815 }
816 }
817 for (Way wOuter : outer)
818 {
819 ElemStyle outerStyle = getPrimitiveStyle(wOuter);
820 if(outerStyle == null)
821 {
822 // Selected ways are drawn at the very end
823 if (data.isSelected(wOuter)) {
824 continue;
825 }
826 if(zoomok)
827 {
828 ((AreaElemStyle)wayStyle).getLineStyle().paintPrimitive(wOuter, paintSettings, painter, (data.isSelected(wOuter) || data.isSelected(r)));
829 }
830 wOuter.mappaintDrawnCode = paintid;
831 }
832 else
833 {
834 if(outerStyle instanceof AreaElemStyle
835 && !wayStyle.equals(outerStyle))
836 {
837 putError(r, tr("Style for outer way ''{0}'' mismatches.",
838 wOuter.getDisplayName(DefaultNameFormatter.getInstance())), true);
839 }
840 if(data.isSelected(r))
841 {
842 drawSelectedMember(wOuter, outerStyle, false, false);
843 }
844 else if(outerStyle instanceof AreaElemStyle) {
845 wOuter.mappaintDrawnAreaCode = paintid;
846 }
847 }
848 }
849 }
850 return drawn;
851 }
852
853 protected boolean isPolygonVisible(Polygon polygon) {
854 Rectangle bounds = polygon.getBounds();
855 if (bounds.width == 0 && bounds.height == 0) return false;
856 if (bounds.x > nc.getWidth()) return false;
857 if (bounds.y > nc.getHeight()) return false;
858 if (bounds.x + bounds.width < 0) return false;
859 if (bounds.y + bounds.height < 0) return false;
860 return true;
861 }
862
863 protected Polygon getPolygon(Way w)
864 {
865 Polygon polygon = new Polygon();
866
867 for (Node n : w.getNodes())
868 {
869 Point p = nc.getPoint(n);
870 polygon.addPoint(p.x,p.y);
871 }
872 return polygon;
873 }
874
875 protected Point2D getCentroid(Polygon p)
876 {
877 double cx = 0.0, cy = 0.0, a = 0.0;
878
879 // usually requires points[0] == points[npoints] and can then use i+1 instead of j.
880 // Faked it slowly using j. If this is really gets used, this should be fixed.
881 for (int i = 0; i < p.npoints; i++) {
882 int j = i+1 == p.npoints ? 0 : i+1;
883 a += (p.xpoints[i] * p.ypoints[j]) - (p.ypoints[i] * p.xpoints[j]);
884 cx += (p.xpoints[i] + p.xpoints[j]) * (p.xpoints[i] * p.ypoints[j] - p.ypoints[i] * p.xpoints[j]);
885 cy += (p.ypoints[i] + p.ypoints[j]) * (p.xpoints[i] * p.ypoints[j] - p.ypoints[i] * p.xpoints[j]);
886 }
887 return new Point2D.Double(cx / (3.0*a), cy / (3.0*a));
888 }
889
890 protected double getArea(Polygon p)
891 {
892 double sum = 0.0;
893
894 // usually requires points[0] == points[npoints] and can then use i+1 instead of j.
895 // Faked it slowly using j. If this is really gets used, this should be fixed.
896 for (int i = 0; i < p.npoints; i++) {
897 int j = i+1 == p.npoints ? 0 : i+1;
898 sum = sum + (p.xpoints[i] * p.ypoints[j]) - (p.ypoints[i] * p.xpoints[j]);
899 }
900 return Math.abs(sum/2.0);
901 }
902
903 DataSet data;
904
905 <T extends OsmPrimitive> Collection<T> selectedLast(final DataSet data, Collection <T> prims) {
906 ArrayList<T> sorted = new ArrayList<T>(prims);
907 Collections.sort(sorted,
908 new Comparator<T>() {
909 public int compare(T o1, T o2) {
910 boolean s1 = data.isSelected(o1);
911 boolean s2 = data.isSelected(o2);
912 if (s1 && !s2)
913 return 1;
914 if (!s1 && s2)
915 return -1;
916 return o1.compareTo(o2);
917 }
918 });
919 return sorted;
920 }
921
922 /* Shows areas before non-areas */
923 public void visitAll(DataSet data, boolean virtual, Bounds bounds) {
924 BBox bbox = new BBox(bounds);
925 this.data = data;
926
927 useStyleCache = Main.pref.getBoolean("mappaint.cache", true);
928 int fillAreas = Main.pref.getInteger("mappaint.fillareas", 10000000);
929 LatLon ll1 = nc.getLatLon(0, 0);
930 LatLon ll2 = nc.getLatLon(100, 0);
931 dist = ll1.greatCircleDistance(ll2);
932
933 zoomLevelDisplay = Main.pref.getBoolean("mappaint.zoomLevelDisplay", false);
934 circum = Main.map.mapView.getDist100Pixel();
935 styles = MapPaintStyles.getStyles().getStyleSet();
936 drawMultipolygon = Main.pref.getBoolean("mappaint.multipolygon", true);
937 drawRestriction = Main.pref.getBoolean("mappaint.restriction", true);
938 leftHandTraffic = Main.pref.getBoolean("mappaint.lefthandtraffic", false);
939 minEN = nc.getEastNorth(0, nc.getHeight() - 1);
940 maxEN = nc.getEastNorth(nc.getWidth() - 1, 0);
941
942 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
943 Main.pref.getBoolean("mappaint.use-antialiasing", false) ?
944 RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
945
946 this.paintSettings = MapPaintSettings.INSTANCE;
947 this.painter = new MapPainter(paintSettings, g, inactive, nc, virtual, dist, circum);
948
949 data.clearErrors();
950
951 ++paintid;
952
953 if (fillAreas > dist && styles != null && styles.hasAreas()) {
954 Collection<Way> noAreaWays = new LinkedList<Way>();
955
956 /*** RELATIONS ***/
957 for (final Relation osm: data.getRelations()) {
958 if (osm.isDrawable()) {
959 paintUnselectedRelation(osm);
960 }
961 }
962
963 /*** AREAS ***/
964 for (final Way osm : selectedLast(data, data.searchWays(bbox))) {
965 if (osm.isDrawable() && osm.mappaintDrawnCode != paintid) {
966 if (isPrimitiveArea(osm) && osm.mappaintDrawnAreaCode != paintid) {
967 drawWay(osm, fillAreas);
968 } else {
969 noAreaWays.add(osm);
970 }
971 }
972 }
973
974 /*** WAYS ***/
975 for (final Way osm : noAreaWays) {
976 drawWay(osm, 0);
977 }
978 } else {
979 drawMultipolygon = false;
980
981 /*** RELATIONS ***/
982 for (final Relation osm: data.getRelations()) {
983 if (osm.isDrawable()) {
984 paintUnselectedRelation(osm);
985 }
986 }
987
988 /*** WAYS (filling disabled) ***/
989 for (final Way way: data.getWays()) {
990 if (way.isDrawable() && !data.isSelected(way)) {
991 drawWay(way, 0);
992 }
993 }
994 }
995
996 /*** SELECTED ***/
997 for (final OsmPrimitive osm : data.getSelected()) {
998 if (osm.isUsable() && !(osm instanceof Node) && osm.mappaintDrawnCode != paintid) {
999 osm.visit(new AbstractVisitor() {
1000 public void visit(Way w) {
1001 drawWay(w, 0);
1002 }
1003
1004 public void visit(Node n) {
1005 // Selected nodes are painted in following part
1006 }
1007
1008 public void visit(Relation r) {
1009 /* TODO: is it possible to do this like the nodes/ways code? */
1010 // Only nodes are painted, ways was already painted before (this might cause that
1011 // way in selected relation is hidden by another way)
1012 for (RelationMember m : r.getMembers()) {
1013 if (m.isNode() && m.getMember().isDrawable()) {
1014 drawSelectedMember(m.getMember(), styles != null ? getPrimitiveStyle(m.getMember()) : null, true, true);
1015 }
1016 }
1017 }
1018 });
1019 }
1020 }
1021
1022 /*** NODES ***/
1023 for (final Node osm: data.searchNodes(bbox)) {
1024 if (!osm.isIncomplete() && !osm.isDeleted() && (data.isSelected(osm) || !osm.isFiltered())
1025 && osm.mappaintDrawnCode != paintid) {
1026 drawNode(osm);
1027 }
1028 }
1029
1030 painter.drawVirtualNodes(data.searchWays(bbox));
1031 }
1032
1033 public void putError(OsmPrimitive p, String text, boolean isError)
1034 {
1035 data.addError(p, isError ? tr("Error: {0}", text) : tr("Warning: {0}", text));
1036 }
1037
1038 public void setGraphics(Graphics2D g) {
1039 this.g = g;
1040 }
1041
1042 public void setInactive(boolean inactive) {
1043 this.inactive = inactive;
1044 }
1045
1046 public void setNavigatableComponent(NavigatableComponent nc) {
1047 this.nc = nc;
1048 }
1049}
Note: See TracBrowser for help on using the repository browser.