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

Last change on this file since 3831 was 3831, checked in by bastiK, 13 years ago

move restriction drawing code to mappainter

  • Property svn:eol-style set to native
File size: 19.2 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.data.osm.visitor.paint;
3
4import java.awt.Graphics2D;
5import java.awt.Polygon;
6import java.awt.Rectangle;
7import java.awt.RenderingHints;
8import java.awt.geom.Point2D;
9import java.util.ArrayList;
10import java.util.Collection;
11import java.util.Collections;
12import java.util.Comparator;
13import java.util.LinkedList;
14
15import org.openstreetmap.josm.Main;
16import org.openstreetmap.josm.data.Bounds;
17import org.openstreetmap.josm.data.coor.EastNorth;
18import org.openstreetmap.josm.data.coor.LatLon;
19import org.openstreetmap.josm.data.osm.BBox;
20import org.openstreetmap.josm.data.osm.DataSet;
21import org.openstreetmap.josm.data.osm.Node;
22import org.openstreetmap.josm.data.osm.OsmPrimitive;
23import org.openstreetmap.josm.data.osm.Relation;
24import org.openstreetmap.josm.data.osm.RelationMember;
25import org.openstreetmap.josm.data.osm.Way;
26import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
27import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
28import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData;
29import org.openstreetmap.josm.gui.NavigatableComponent;
30import org.openstreetmap.josm.gui.mappaint.AreaElemStyle;
31import org.openstreetmap.josm.gui.mappaint.ElemStyle;
32import org.openstreetmap.josm.gui.mappaint.ElemStyles;
33import org.openstreetmap.josm.gui.mappaint.IconElemStyle;
34import org.openstreetmap.josm.gui.mappaint.LineElemStyle;
35import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
36import org.openstreetmap.josm.gui.mappaint.StyleCache;
37
38public class MapPaintVisitor implements PaintVisitor {
39
40 private Graphics2D g;
41 private NavigatableComponent nc;
42
43 private boolean zoomLevelDisplay;
44 private boolean drawMultipolygon;
45 private boolean drawRestriction;
46 private boolean leftHandTraffic;
47 private ElemStyles styles;
48 private double circum;
49 private double dist;
50 private static int paintid = 0;
51 private EastNorth minEN;
52 private EastNorth maxEN;
53 private MapPainter painter;
54 private MapPaintSettings paintSettings;
55
56 private boolean inactive;
57
58 protected boolean isZoomOk(ElemStyle e) {
59 if (!zoomLevelDisplay) /* show everything if the user wishes so */
60 return true;
61
62 if(e == null) /* the default for things that don't have a rule (show, if scale is smaller than 1500m) */
63 return (circum < 1500);
64
65 return !(circum >= e.maxScale || circum < e.minScale);
66 }
67
68 public StyleCache getPrimitiveStyle(OsmPrimitive osm, boolean nodefault) {
69 if(osm.mappaintStyle == null)
70 {
71 if(styles != null) {
72 osm.mappaintStyle = styles.get(osm);
73 if(osm instanceof Way) {
74 ((Way)osm).isMappaintArea = styles.isArea(osm);
75 }
76 }
77 if (osm.mappaintStyle.equals(StyleCache.EMPTY_STYLECACHE)) {
78 if(osm instanceof Node)
79 osm.mappaintStyle = StyleCache.SIMPLE_NODE_STYLECACHE;
80 else if (osm instanceof Way)
81 osm.mappaintStyle = StyleCache.UNTAGGED_WAY_STYLECACHE;
82 }
83 }
84 if (nodefault && osm.mappaintStyle.equals(StyleCache.UNTAGGED_WAY_STYLECACHE))
85 return StyleCache.EMPTY_STYLECACHE;
86 return osm.mappaintStyle;
87 }
88
89 public IconElemStyle getPrimitiveNodeStyle(OsmPrimitive osm) {
90 if(osm.mappaintStyle == null && styles != null) {
91 IconElemStyle icon = styles.getIcon(osm);
92 osm.mappaintStyle = StyleCache.create(icon);
93 return icon;
94 }
95 for (ElemStyle s : osm.mappaintStyle.getStyles()) {
96 if (s instanceof IconElemStyle)
97 return (IconElemStyle) s;
98 }
99 return null;
100 }
101
102 public boolean isPrimitiveArea(Way osm) {
103 if(osm.mappaintStyle == null && styles != null) {
104 osm.mappaintStyle = styles.get(osm);
105 osm.isMappaintArea = styles.isArea(osm);
106 }
107 return osm.isMappaintArea;
108 }
109
110 public void drawNode(Node n) {
111 /* check, if the node is visible at all */
112 EastNorth en = n.getEastNorth();
113 if((en.east() > maxEN.east() ) ||
114 (en.north() > maxEN.north()) ||
115 (en.east() < minEN.east() ) ||
116 (en.north() < minEN.north()))
117 return;
118
119 StyleCache sc = getPrimitiveStyle(n, false);
120
121 for (ElemStyle s : sc.getStyles()) {
122 if (isZoomOk(s)) {
123 s.paintPrimitive(n, paintSettings, painter, data.isSelected(n), false);
124 }
125
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 StyleCache sc = getPrimitiveStyle(w, false);
165 for (ElemStyle s : sc.getStyles()) {
166 if(!isZoomOk(s))
167 return;
168 if (fillAreas > dist || !(s instanceof AreaElemStyle)) {
169 s.paintPrimitive(w, paintSettings, painter, data.isSelected(w), false);
170 }
171 }
172 }
173
174 public void paintUnselectedRelation(Relation r) {
175 if (drawMultipolygon && "multipolygon".equals(r.get("type")))
176 drawMultipolygon(r);
177 else if (drawRestriction && "restriction".equals(r.get("type"))) {
178 IconElemStyle nodeStyle = getPrimitiveNodeStyle(r);
179 if (nodeStyle != null) {
180 painter.drawRestriction(r, leftHandTraffic, nodeStyle);
181 }
182 }
183 }
184
185 public boolean drawMultipolygon(Relation r) {
186 boolean drawn = false;
187
188 Multipolygon multipolygon = new Multipolygon(nc);
189 multipolygon.load(r);
190
191 AreaElemStyle areaStyle = null;
192 LineElemStyle lineStyle = null;
193 for (ElemStyle s : getPrimitiveStyle(r, false).getStyles()) {
194 if (s instanceof AreaElemStyle) {
195 areaStyle = (AreaElemStyle) s;
196 } else if (s instanceof LineElemStyle) {
197 lineStyle = (LineElemStyle) s;
198 }
199 }
200
201 boolean disabled = r.isDisabled();
202 // If area style was not found for relation then use style of ways
203 if(styles != null && areaStyle == null) {
204 for (Way w : multipolygon.getOuterWays()) {
205 for (ElemStyle s : styles.getArea(w).getStyles()) {
206 if (s instanceof AreaElemStyle) {
207 areaStyle = (AreaElemStyle) s;
208 } else if (s instanceof LineElemStyle) {
209 lineStyle = (LineElemStyle) s;
210 }
211 }
212 disabled = disabled || w.isDisabled();
213 if(areaStyle != null) {
214 break;
215 }
216 }
217 }
218
219 if (areaStyle != null) {
220 boolean zoomok = isZoomOk(areaStyle);
221 boolean visible = false;
222
223 drawn = true;
224
225 if(zoomok && !disabled && !multipolygon.getOuterWays().isEmpty()) {
226 for (PolyData pd : multipolygon.getCombinedPolygons()) {
227 Polygon p = pd.get();
228 if(!isPolygonVisible(p)) {
229 continue;
230 }
231
232 boolean selected = pd.selected || data.isSelected(r);
233 painter.drawArea(p, selected ? paintSettings.getRelationSelectedColor()
234 : areaStyle.color, painter.getAreaName(r));
235 visible = true;
236 }
237 }
238
239 if(!visible)
240 return drawn;
241 for (Way wInner : multipolygon.getInnerWays()) {
242 StyleCache inner = getPrimitiveStyle(wInner, true);
243 AreaElemStyle innerArea = null;
244 for (ElemStyle s : inner.getStyles()) {
245 if (s instanceof AreaElemStyle) {
246 innerArea = (AreaElemStyle) s;
247 break;
248 }
249 }
250
251 if(inner.getStyles().isEmpty()) {
252 if (data.isSelected(wInner) || disabled)
253 continue;
254 if(zoomok && (wInner.mappaintDrawnCode != paintid || multipolygon.getOuterWays().isEmpty())) {
255 lineStyle.paintPrimitive(wInner, paintSettings,
256 painter, (data.isSelected(wInner) || data.isSelected(r)), false);
257 }
258 wInner.mappaintDrawnCode = paintid;
259 }
260 else {
261 if(areaStyle.equals(innerArea)) {
262 wInner.mappaintDrawnAreaCode = paintid;
263
264 if(!data.isSelected(wInner)) {
265 wInner.mappaintDrawnCode = paintid;
266 drawWay(wInner, 0);
267 }
268 }
269 }
270 }
271 for (Way wOuter : multipolygon.getOuterWays()) {
272 StyleCache outer = getPrimitiveStyle(wOuter, true);
273 boolean hasOuterArea = false;
274 for (ElemStyle s : outer.getStyles()) {
275 if (s instanceof AreaElemStyle) {
276 hasOuterArea = true;
277 break;
278 }
279 }
280
281 if (outer.getStyles().isEmpty()) {
282 // Selected ways are drawn at the very end
283 if (data.isSelected(wOuter))
284 continue;
285 if(zoomok) {
286 lineStyle.paintPrimitive(wOuter, paintSettings, painter,
287 (data.isSelected(wOuter) || data.isSelected(r)), r.isSelected());
288 }
289 wOuter.mappaintDrawnCode = paintid;
290 } else if (hasOuterArea) {
291 wOuter.mappaintDrawnAreaCode = paintid;
292 if(!data.isSelected(wOuter)) {
293 wOuter.mappaintDrawnCode = paintid;
294 drawWay(wOuter, 0);
295 }
296 }
297 }
298 }
299 return drawn;
300 }
301
302 protected boolean isPolygonVisible(Polygon polygon) {
303 Rectangle bounds = polygon.getBounds();
304 if (bounds.width == 0 && bounds.height == 0) return false;
305 if (bounds.x > nc.getWidth()) return false;
306 if (bounds.y > nc.getHeight()) return false;
307 if (bounds.x + bounds.width < 0) return false;
308 if (bounds.y + bounds.height < 0) return false;
309 return true;
310 }
311
312 protected Point2D getCentroid(Polygon p)
313 {
314 double cx = 0.0, cy = 0.0, a = 0.0;
315
316 // usually requires points[0] == points[npoints] and can then use i+1 instead of j.
317 // Faked it slowly using j. If this is really gets used, this should be fixed.
318 for (int i = 0; i < p.npoints; i++) {
319 int j = i+1 == p.npoints ? 0 : i+1;
320 a += (p.xpoints[i] * p.ypoints[j]) - (p.ypoints[i] * p.xpoints[j]);
321 cx += (p.xpoints[i] + p.xpoints[j]) * (p.xpoints[i] * p.ypoints[j] - p.ypoints[i] * p.xpoints[j]);
322 cy += (p.ypoints[i] + p.ypoints[j]) * (p.xpoints[i] * p.ypoints[j] - p.ypoints[i] * p.xpoints[j]);
323 }
324 return new Point2D.Double(cx / (3.0*a), cy / (3.0*a));
325 }
326
327 protected double getArea(Polygon p)
328 {
329 double sum = 0.0;
330
331 // usually requires points[0] == points[npoints] and can then use i+1 instead of j.
332 // Faked it slowly using j. If this is really gets used, this should be fixed.
333 for (int i = 0; i < p.npoints; i++) {
334 int j = i+1 == p.npoints ? 0 : i+1;
335 sum = sum + (p.xpoints[i] * p.ypoints[j]) - (p.ypoints[i] * p.xpoints[j]);
336 }
337 return Math.abs(sum/2.0);
338 }
339
340 DataSet data;
341
342 <T extends OsmPrimitive> Collection<T> selectedLast(final DataSet data, Collection <T> prims) {
343 ArrayList<T> sorted = new ArrayList<T>(prims);
344 Collections.sort(sorted,
345 new Comparator<T>() {
346 public int compare(T o1, T o2) {
347 boolean s1 = data.isSelected(o1);
348 boolean s2 = data.isSelected(o2);
349 if (s1 && !s2)
350 return 1;
351 if (!s1 && s2)
352 return -1;
353 return o1.compareTo(o2);
354 }
355 });
356 return sorted;
357 }
358
359 /* Shows areas before non-areas */
360 public void visitAll(final DataSet data, boolean virtual, Bounds bounds) {
361 //long start = System.currentTimeMillis();
362 BBox bbox = new BBox(bounds);
363 this.data = data;
364 ++paintid;
365
366 int fillAreas = Main.pref.getInteger("mappaint.fillareas", 10000000);
367 LatLon ll1 = nc.getLatLon(0, 0);
368 LatLon ll2 = nc.getLatLon(100, 0);
369 dist = ll1.greatCircleDistance(ll2);
370
371 zoomLevelDisplay = Main.pref.getBoolean("mappaint.zoomLevelDisplay", false);
372 circum = nc.getDist100Pixel();
373 styles = MapPaintStyles.getStyles();
374 drawMultipolygon = Main.pref.getBoolean("mappaint.multipolygon", true);
375 drawRestriction = Main.pref.getBoolean("mappaint.restriction", true);
376 leftHandTraffic = Main.pref.getBoolean("mappaint.lefthandtraffic", false);
377 minEN = nc.getEastNorth(0, nc.getHeight() - 1);
378 maxEN = nc.getEastNorth(nc.getWidth() - 1, 0);
379
380 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
381 Main.pref.getBoolean("mappaint.use-antialiasing", false) ?
382 RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
383
384 this.paintSettings = MapPaintSettings.INSTANCE;
385 this.painter = new MapPainter(paintSettings, g, inactive, nc, virtual, dist, circum);
386
387 if (fillAreas > dist && styles != null && styles.hasAreas()) {
388 Collection<Way> noAreaWays = new LinkedList<Way>();
389 final Collection<Way> ways = data.searchWays(bbox);
390
391 /*** disabled ***/
392 for (final Way osm : ways) {
393 if (osm.isDisabled() && osm.isDrawable() && osm.mappaintDrawnCode != paintid) {
394 drawWay(osm, 0);
395 osm.mappaintDrawnCode = paintid;
396 }
397 }
398
399 /*** RELATIONS ***/
400 for (final Relation osm: data.searchRelations(bbox)) {
401 if (osm.isDrawable()) {
402 paintUnselectedRelation(osm);
403 }
404 }
405
406 /*** AREAS ***/
407 for (final Way osm : selectedLast(data, ways)) {
408 if (osm.isDrawable() && osm.mappaintDrawnCode != paintid) {
409 if (isPrimitiveArea(osm)) {
410 if(osm.mappaintDrawnAreaCode != paintid)
411 drawWay(osm, fillAreas);
412 } else if(!data.isSelected(osm)) {
413 noAreaWays.add(osm);
414 }
415 }
416 }
417
418 /*** WAYS ***/
419 for (final Way osm : noAreaWays) {
420 drawWay(osm, 0);
421 osm.mappaintDrawnCode = paintid;
422 }
423 } else {
424 drawMultipolygon = false;
425 final Collection<Way> ways = data.searchWays(bbox);
426
427 /*** WAYS (disabled) ***/
428 for (final Way way: ways) {
429 if (way.isDisabled() && way.isDrawable() && !data.isSelected(way)) {
430 drawWay(way, 0);
431 way.mappaintDrawnCode = paintid;
432 }
433 }
434
435 /*** RELATIONS ***/
436 for (final Relation osm: data.searchRelations(bbox)) {
437 if (osm.isDrawable()) {
438 paintUnselectedRelation(osm);
439 }
440 }
441
442 /*** WAYS (filling disabled) ***/
443 for (final Way way: ways) {
444 if (way.isDrawable() && !data.isSelected(way)) {
445 drawWay(way, 0);
446 }
447 }
448 }
449
450 /*** SELECTED ***/
451 for (final OsmPrimitive osm : data.getSelected()) {
452 if (osm.isUsable() && !(osm instanceof Node) && (osm instanceof Relation || osm.mappaintDrawnCode != paintid)) {
453 osm.visit(new AbstractVisitor() {
454 public void visit(Way w) {
455 drawWay(w, 0);
456 }
457
458 public void visit(Node n) {
459 // Selected nodes are painted in following part
460 }
461
462 public void visit(Relation r) {
463 for (RelationMember m : r.getMembers()) {
464 OsmPrimitive osm = m.getMember();
465 if(osm.isDrawable()) {
466 StyleCache sc = getPrimitiveStyle(m.getMember(), false);
467 if(osm instanceof Way)
468 {
469 for (ElemStyle s : sc.getStyles()) {
470 if (!(s instanceof AreaElemStyle)) {
471 s.paintPrimitive(osm, paintSettings, painter, data.isSelected(osm), true);
472 }
473 }
474 }
475 else if(osm instanceof Node)
476 {
477 for (ElemStyle s : sc.getStyles()) {
478 if (isZoomOk(s)) {
479 s.paintPrimitive(osm, paintSettings, painter, data.isSelected(osm), true);
480 }
481 }
482 }
483 osm.mappaintDrawnCode = paintid;
484 }
485 }
486 }
487 });
488 }
489 }
490
491 /*** NODES ***/
492 for (final Node osm: data.searchNodes(bbox)) {
493 if (!osm.isIncomplete() && !osm.isDeleted() && (data.isSelected(osm) || !osm.isDisabledAndHidden())
494 && osm.mappaintDrawnCode != paintid) {
495 drawNode(osm);
496 }
497 }
498
499 painter.drawVirtualNodes(data.searchWays(bbox));
500 //System.err.println("PAINTING TOOK "+(System.currentTimeMillis() - start));
501 }
502
503 public void setGraphics(Graphics2D g) {
504 this.g = g;
505 }
506
507 public void setInactive(boolean inactive) {
508 this.inactive = inactive;
509 }
510
511 public void setNavigatableComponent(NavigatableComponent nc) {
512 this.nc = nc;
513 }
514}
Note: See TracBrowser for help on using the repository browser.