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

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

MapPaintVisitor - delegate drawing to styles, MapPaintVisitor should only select correct style and then let primitives draw in correct order. (not finished yet)

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