source: josm/trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/MapPainter.java@ 3860

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

LineElemStyle refactoring (more caching and additional properties)

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/plain
File size: 23.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm.visitor.paint;
3
4import java.awt.BasicStroke;
5import java.awt.Color;
6import java.awt.Font;
7import java.awt.FontMetrics;
8import java.awt.Graphics2D;
9import java.awt.Image;
10import java.awt.Point;
11import java.awt.Polygon;
12import java.awt.Rectangle;
13import java.awt.geom.GeneralPath;
14import java.awt.geom.Rectangle2D;
15import java.util.Arrays;
16import java.util.Collection;
17import java.util.Iterator;
18
19import javax.swing.ImageIcon;
20
21import org.openstreetmap.josm.Main;
22import org.openstreetmap.josm.data.osm.Node;
23import org.openstreetmap.josm.data.osm.OsmPrimitive;
24import org.openstreetmap.josm.data.osm.OsmUtils;
25import org.openstreetmap.josm.data.osm.Relation;
26import org.openstreetmap.josm.data.osm.RelationMember;
27import org.openstreetmap.josm.data.osm.Way;
28import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
29import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData;
30import org.openstreetmap.josm.gui.NavigatableComponent;
31import org.openstreetmap.josm.gui.mappaint.NodeElemStyle;
32import org.openstreetmap.josm.tools.ImageProvider;
33import org.openstreetmap.josm.tools.LanguageInfo;
34
35public class MapPainter {
36
37 private final Graphics2D g;
38 private final NavigatableComponent nc;
39 private final boolean inactive;
40
41 private final boolean useStrokes;
42 private final boolean showNames;
43 private final boolean showIcons;
44 private final boolean outlineOnly;
45
46 private final Color inactiveColor;
47 private final Color textColor;
48 private final Color selectedColor;
49 private final Color relationSelectedColor;
50 private final Color areaTextColor;
51 private final Color nodeColor;
52 private final Color backgroundColor;
53
54 private final Font orderFont;
55 private final int fillAlpha;
56 private final int virtualNodeSize;
57 private final int virtualNodeSpace;
58 private final int segmentNumberSpace;
59
60 private final double circum;
61
62 private final boolean leftHandTraffic;
63
64 private final Collection<String> regionalNameOrder;
65
66 private static final double PHI = Math.toRadians(20);
67 private static final double cosPHI = Math.cos(PHI);
68 private static final double sinPHI = Math.sin(PHI);
69
70 public MapPainter(MapPaintSettings settings, Graphics2D g,
71 boolean inactive, NavigatableComponent nc, boolean virtual,
72 double circum, boolean leftHandTraffic)
73 {
74 this.g = g;
75 this.inactive = inactive;
76 this.nc = nc;
77 this.useStrokes = settings.getUseStrokesDistance() > circum;
78 this.showNames = settings.getShowNamesDistance() > circum;
79 this.showIcons = settings.getShowIconsDistance() > circum;
80 this.outlineOnly = settings.isOutlineOnly();
81
82 this.inactiveColor = PaintColors.INACTIVE.get();
83 this.textColor = PaintColors.TEXT.get();
84 this.selectedColor = PaintColors.SELECTED.get();
85 this.relationSelectedColor = PaintColors.RELATIONSELECTED.get();
86 this.areaTextColor = PaintColors.AREA_TEXT.get();
87 this.nodeColor = PaintColors.NODE.get();
88 this.backgroundColor = PaintColors.BACKGROUND.get();
89
90 this.orderFont = new Font(Main.pref.get("mappaint.font", "Helvetica"), Font.PLAIN, Main.pref.getInteger("mappaint.fontsize", 8));
91 this.fillAlpha = Math.min(255, Math.max(0, Integer.valueOf(Main.pref.getInteger("mappaint.fillalpha", 50))));
92 this.virtualNodeSize = virtual ? Main.pref.getInteger("mappaint.node.virtual-size", 8) / 2 : 0;
93 this.virtualNodeSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70);
94 this.segmentNumberSpace = Main.pref.getInteger("mappaint.segmentnumber.space", 40);
95
96 String[] names = {"name:" + LanguageInfo.getJOSMLocaleCode(), "name", "int_name", "ref", "operator", "brand", "addr:housenumber"};
97 this.regionalNameOrder = Main.pref.getCollection("mappaint.nameOrder", Arrays.asList(names));
98 this.circum = circum;
99 this.leftHandTraffic = leftHandTraffic;
100 }
101
102 public void drawWay(Way way, Color color, BasicStroke line, BasicStroke dashes, Color dashedColor, boolean showDirection,
103 boolean reversedDirection, boolean showHeadArrowOnly) {
104
105 GeneralPath path = new GeneralPath();
106 GeneralPath arrows = new GeneralPath();
107 Rectangle bounds = g.getClipBounds();
108 bounds.grow(100, 100); // avoid arrow heads at the border
109
110 Point lastPoint = null;
111 boolean initialMoveToNeeded = true;
112 Iterator<Node> it = way.getNodes().iterator();
113 while (it.hasNext()) {
114 Node n = it.next();
115 Point p = nc.getPoint(n);
116 if(lastPoint != null) {
117 Point p1 = lastPoint;
118 Point p2 = p;
119
120 /**
121 * Do custom clipping to work around openjdk bug. It leads to
122 * drawing artefacts when zooming in a lot. (#4289, #4424)
123 * (Looks like int overflow.)
124 */
125 LineClip clip = new LineClip(p1, p2, bounds);
126 if (clip.execute()) {
127 if (!p1.equals(clip.getP1())) {
128 p1 = clip.getP1();
129 path.moveTo(p1.x, p1.y);
130 } else if (initialMoveToNeeded) {
131 initialMoveToNeeded = false;
132 path.moveTo(p1.x, p1.y);
133 }
134 p2 = clip.getP2();
135 path.lineTo(p2.x, p2.y);
136
137 /* draw arrow */
138 if (showHeadArrowOnly ? !it.hasNext() : showDirection) {
139 if (reversedDirection) {
140 Point tmp = p1;
141 p1 = p2;
142 p2 = tmp;
143 }
144 final double l = 10. / p1.distance(p2);
145
146 final double sx = l * (p1.x - p2.x);
147 final double sy = l * (p1.y - p2.y);
148
149 arrows.moveTo(p2.x, p2.y);
150 arrows.lineTo (p2.x + (int) Math.round(cosPHI * sx - sinPHI * sy), p2.y + (int) Math.round(sinPHI * sx + cosPHI * sy));
151 arrows.moveTo (p2.x + (int) Math.round(cosPHI * sx + sinPHI * sy), p2.y + (int) Math.round(- sinPHI * sx + cosPHI * sy));
152 arrows.lineTo(p2.x, p2.y);
153 }
154 }
155 }
156 lastPoint = p;
157 }
158 displaySegments(path, arrows, color, line, dashes, dashedColor);
159 }
160
161 private void displaySegments(GeneralPath path, GeneralPath arrows, Color color, BasicStroke line, BasicStroke dashes, Color dashedColor) {
162 g.setColor(inactive ? inactiveColor : color);
163 if (useStrokes) {
164 g.setStroke(line);
165 }
166 g.draw(path);
167 g.draw(arrows);
168
169 if(!inactive && useStrokes && dashes != null) {
170 g.setColor(dashedColor);
171 g.setStroke(dashes);
172 g.draw(path);
173 g.draw(arrows);
174 }
175
176 if(useStrokes) {
177 g.setStroke(new BasicStroke());
178 }
179 }
180
181 private boolean isSegmentVisible(Point p1, Point p2) {
182 if ((p1.x < 0) && (p2.x < 0)) return false;
183 if ((p1.y < 0) && (p2.y < 0)) return false;
184 if ((p1.x > nc.getWidth()) && (p2.x > nc.getWidth())) return false;
185 if ((p1.y > nc.getHeight()) && (p2.y > nc.getHeight())) return false;
186 return true;
187 }
188
189 public void drawNodeIcon(Node n, ImageIcon icon, boolean selected, boolean member, String name) {
190 Point p = nc.getPoint(n);
191 if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth()) || (p.y > nc.getHeight())) return;
192
193 int w = icon.getIconWidth(), h=icon.getIconHeight();
194 icon.paintIcon ( nc, g, p.x-w/2, p.y-h/2 );
195 if(name != null) {
196 if (inactive || n.isDisabled()) {
197 g.setColor(inactiveColor);
198 } else {
199 g.setColor(textColor);
200 }
201 Font defaultFont = g.getFont();
202 g.setFont (orderFont);
203 g.drawString (name, p.x+w/2+2, p.y+h/2+2);
204 g.setFont(defaultFont);
205 }
206 if (selected || member)
207 {
208 g.setColor(selected? selectedColor : relationSelectedColor);
209 g.drawRect(p.x-w/2-2, p.y-h/2-2, w+4, h+4);
210 }
211 }
212
213 /**
214 * Draw the node as small rectangle with the given color.
215 *
216 * @param n The node to draw.
217 * @param color The color of the node.
218 */
219 public void drawNode(Node n, Color color, int size, boolean fill, String name) {
220 if (size > 1) {
221 Point p = nc.getPoint(n);
222 if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth()) || (p.y > nc.getHeight())) return;
223 int radius = size / 2;
224
225 if (inactive || n.isDisabled()) {
226 g.setColor(inactiveColor);
227 } else {
228 g.setColor(color);
229 }
230 if (fill) {
231 g.fillRect(p.x - radius, p.y - radius, size + 1, size + 1);
232 } else {
233 g.drawRect(p.x - radius, p.y - radius, size, size);
234 }
235
236 if(name != null) {
237 if (inactive || n.isDisabled()) {
238 g.setColor(inactiveColor);
239 } else {
240 g.setColor(textColor);
241 }
242 Font defaultFont = g.getFont();
243 g.setFont (orderFont);
244 g.drawString (name, p.x+radius+2, p.y+radius+2);
245 g.setFont(defaultFont);
246 }
247 }
248 }
249
250 private Polygon getPolygon(Way w) {
251 Polygon polygon = new Polygon();
252
253 for (Node n : w.getNodes()) {
254 Point p = nc.getPoint(n);
255 polygon.addPoint(p.x,p.y);
256 }
257 return polygon;
258 }
259
260 public void drawArea(Way w, Color color, String name) {
261 Polygon polygon = getPolygon(w);
262 drawArea(polygon, color, name);
263 }
264
265 protected void drawArea(Polygon polygon, Color color, String name) {
266
267 g.setColor(color);
268
269 if (outlineOnly) {
270 g.drawPolygon(polygon);
271 } else {
272 g.fillPolygon(polygon);
273 }
274
275
276 if (name != null) {
277 Rectangle pb = polygon.getBounds();
278 FontMetrics fontMetrics = g.getFontMetrics(orderFont); // if slow, use cache
279 Rectangle2D nb = fontMetrics.getStringBounds(name, g); // if slow, approximate by strlen()*maxcharbounds(font)
280
281 // Point2D c = getCentroid(polygon);
282 // Using the Centroid is Nicer for buildings like: +--------+
283 // but this needs to be fast. As most houses are | 42 |
284 // boxes anyway, the center of the bounding box +---++---+
285 // will have to do. ++
286 // Centroids are not optimal either, just imagine a U-shaped house.
287 // Point2D c = new Point2D.Double(pb.x + pb.width / 2.0, pb.y + pb.height / 2.0);
288 // Rectangle2D.Double centeredNBounds =
289 // new Rectangle2D.Double(c.getX() - nb.getWidth()/2,
290 // c.getY() - nb.getHeight()/2,
291 // nb.getWidth(),
292 // nb.getHeight());
293
294 Rectangle centeredNBounds = new Rectangle(pb.x + (int)((pb.width - nb.getWidth())/2.0),
295 pb.y + (int)((pb.height - nb.getHeight())/2.0),
296 (int)nb.getWidth(),
297 (int)nb.getHeight());
298
299 if ((pb.width >= nb.getWidth() && pb.height >= nb.getHeight()) && // quick check
300 polygon.contains(centeredNBounds) // slow but nice
301 ) {
302 g.setColor(areaTextColor);
303 Font defaultFont = g.getFont();
304 g.setFont (orderFont);
305 g.drawString (name,
306 (int)(centeredNBounds.getMinX() - nb.getMinX()),
307 (int)(centeredNBounds.getMinY() - nb.getMinY()));
308 g.setFont(defaultFont);
309 }
310 }
311 }
312
313 public void drawArea(Relation r, Color color, String name) {
314 Multipolygon multipolygon = new Multipolygon(nc);
315 multipolygon.load(r);
316 if(!r.isDisabled() && !multipolygon.getOuterWays().isEmpty()) {
317 for (PolyData pd : multipolygon.getCombinedPolygons()) {
318 Polygon p = pd.get();
319 if(!isPolygonVisible(p)) {
320 continue;
321 }
322 drawArea(p, color, getAreaName(r));
323 }
324 }
325 }
326
327 private boolean isPolygonVisible(Polygon polygon) {
328 Rectangle bounds = polygon.getBounds();
329 if (bounds.width == 0 && bounds.height == 0) return false;
330 if (bounds.x > nc.getWidth()) return false;
331 if (bounds.y > nc.getHeight()) return false;
332 if (bounds.x + bounds.width < 0) return false;
333 if (bounds.y + bounds.height < 0) return false;
334 return true;
335 }
336
337 public void drawRestriction(ImageIcon icon, Point pVia, double vx, double vx2, double vy, double vy2, double iconAngle, boolean selected) {
338 /* rotate icon with direction last node in from to */
339 ImageIcon rotatedIcon = ImageProvider.createRotatedImage(null /*icon2*/, icon, iconAngle);
340
341 /* scale down icon to 16*16 pixels */
342 ImageIcon smallIcon = new ImageIcon(rotatedIcon.getImage().getScaledInstance(16 , 16, Image.SCALE_SMOOTH));
343 int w = smallIcon.getIconWidth(), h=smallIcon.getIconHeight();
344 smallIcon.paintIcon (nc, g, (int)(pVia.x+vx+vx2)-w/2, (int)(pVia.y+vy+vy2)-h/2 );
345
346 if (selected) {
347 g.setColor(relationSelectedColor);
348 g.drawRect((int)(pVia.x+vx+vx2)-w/2-2,(int)(pVia.y+vy+vy2)-h/2-2, w+4, h+4);
349 }
350 }
351
352 public void drawRestriction(Relation r, NodeElemStyle icon) {
353
354 Way fromWay = null;
355 Way toWay = null;
356 OsmPrimitive via = null;
357
358 /* find the "from", "via" and "to" elements */
359 for (RelationMember m : r.getMembers())
360 {
361 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 continue;
370 }
371
372 if("from".equals(m.getRole())) {
373 if(fromWay == null) {
374 fromWay = w;
375 }
376 } else if("to".equals(m.getRole())) {
377 if(toWay == null) {
378 toWay = w;
379 }
380 } else if("via".equals(m.getRole())) {
381 if(via == null) {
382 via = w;
383 }
384 }
385 }
386 else if(m.isNode())
387 {
388 Node n = m.getNode();
389 if("via".equals(m.getRole()) && via == null) {
390 via = n;
391 }
392 }
393 }
394 }
395
396 if (fromWay == null || toWay == null || via == null)
397 return;
398
399 Node viaNode;
400 if(via instanceof Node)
401 {
402 viaNode = (Node) via;
403 if(!fromWay.isFirstLastNode(viaNode))
404 return;
405 }
406 else
407 {
408 Way viaWay = (Way) via;
409 Node firstNode = viaWay.firstNode();
410 Node lastNode = viaWay.lastNode();
411 Boolean onewayvia = false;
412
413 String onewayviastr = viaWay.get("oneway");
414 if(onewayviastr != null)
415 {
416 if("-1".equals(onewayviastr)) {
417 onewayvia = true;
418 Node tmp = firstNode;
419 firstNode = lastNode;
420 lastNode = tmp;
421 } else {
422 onewayvia = OsmUtils.getOsmBoolean(onewayviastr);
423 if (onewayvia == null) {
424 onewayvia = false;
425 }
426 }
427 }
428
429 if(fromWay.isFirstLastNode(firstNode)) {
430 viaNode = firstNode;
431 } else if (!onewayvia && fromWay.isFirstLastNode(lastNode)) {
432 viaNode = lastNode;
433 } else
434 return;
435 }
436
437 /* find the "direct" nodes before the via node */
438 Node fromNode = null;
439 if(fromWay.firstNode() == via) {
440 fromNode = fromWay.getNode(1);
441 } else {
442 fromNode = fromWay.getNode(fromWay.getNodesCount()-2);
443 }
444
445 Point pFrom = nc.getPoint(fromNode);
446 Point pVia = nc.getPoint(viaNode);
447
448 /* starting from via, go back the "from" way a few pixels
449 (calculate the vector vx/vy with the specified length and the direction
450 away from the "via" node along the first segment of the "from" way)
451 */
452 double distanceFromVia=14;
453 double dx = (pFrom.x >= pVia.x) ? (pFrom.x - pVia.x) : (pVia.x - pFrom.x);
454 double dy = (pFrom.y >= pVia.y) ? (pFrom.y - pVia.y) : (pVia.y - pFrom.y);
455
456 double fromAngle;
457 if(dx == 0.0) {
458 fromAngle = Math.PI/2;
459 } else {
460 fromAngle = Math.atan(dy / dx);
461 }
462 double fromAngleDeg = Math.toDegrees(fromAngle);
463
464 double vx = distanceFromVia * Math.cos(fromAngle);
465 double vy = distanceFromVia * Math.sin(fromAngle);
466
467 if(pFrom.x < pVia.x) {
468 vx = -vx;
469 }
470 if(pFrom.y < pVia.y) {
471 vy = -vy;
472 }
473
474 /* go a few pixels away from the way (in a right angle)
475 (calculate the vx2/vy2 vector with the specified length and the direction
476 90degrees away from the first segment of the "from" way)
477 */
478 double distanceFromWay=10;
479 double vx2 = 0;
480 double vy2 = 0;
481 double iconAngle = 0;
482
483 if(pFrom.x >= pVia.x && pFrom.y >= pVia.y) {
484 if(!leftHandTraffic) {
485 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg - 90));
486 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg - 90));
487 } else {
488 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 90));
489 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 90));
490 }
491 iconAngle = 270+fromAngleDeg;
492 }
493 if(pFrom.x < pVia.x && pFrom.y >= pVia.y) {
494 if(!leftHandTraffic) {
495 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg));
496 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg));
497 } else {
498 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 180));
499 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 180));
500 }
501 iconAngle = 90-fromAngleDeg;
502 }
503 if(pFrom.x < pVia.x && pFrom.y < pVia.y) {
504 if(!leftHandTraffic) {
505 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 90));
506 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 90));
507 } else {
508 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg - 90));
509 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg - 90));
510 }
511 iconAngle = 90+fromAngleDeg;
512 }
513 if(pFrom.x >= pVia.x && pFrom.y < pVia.y) {
514 if(!leftHandTraffic) {
515 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 180));
516 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 180));
517 } else {
518 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg));
519 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg));
520 }
521 iconAngle = 270-fromAngleDeg;
522 }
523
524 drawRestriction(inactive || r.isDisabled() ? icon.getDisabledIcon() : icon.icon,
525 pVia, vx, vx2, vy, vy2, iconAngle, r.isSelected());
526 }
527
528 public void drawVirtualNodes(Collection<Way> ways) {
529
530 if (virtualNodeSize != 0) {
531 GeneralPath path = new GeneralPath();
532 for (Way osm: ways){
533 if (osm.isUsable() && !osm.isDisabled()) {
534 visitVirtual(path, osm);
535 }
536 }
537 g.setColor(nodeColor);
538 g.draw(path);
539 }
540 }
541
542 public void visitVirtual(GeneralPath path, Way w) {
543 Iterator<Node> it = w.getNodes().iterator();
544 if (it.hasNext()) {
545 Point lastP = nc.getPoint(it.next());
546 while(it.hasNext())
547 {
548 Point p = nc.getPoint(it.next());
549 if(isSegmentVisible(lastP, p) && isLargeSegment(lastP, p, virtualNodeSpace))
550 {
551 int x = (p.x+lastP.x)/2;
552 int y = (p.y+lastP.y)/2;
553 path.moveTo(x-virtualNodeSize, y);
554 path.lineTo(x+virtualNodeSize, y);
555 path.moveTo(x, y-virtualNodeSize);
556 path.lineTo(x, y+virtualNodeSize);
557 }
558 lastP = p;
559 }
560 }
561 }
562
563 private static boolean isLargeSegment(Point p1, Point p2, int space) {
564 int xd = p1.x-p2.x; if(xd < 0) {
565 xd = -xd;
566 }
567 int yd = p1.y-p2.y; if(yd < 0) {
568 yd = -yd;
569 }
570 return (xd+yd > space);
571 }
572
573 /**
574 * Draw a number of the order of the two consecutive nodes within the
575 * parents way
576 */
577 public void drawOrderNumber(Node n1, Node n2, int orderNumber) {
578 Point p1 = nc.getPoint(n1);
579 Point p2 = nc.getPoint(n2);
580 drawOrderNumber(p1, p2, orderNumber);
581 }
582
583 /**
584 * Draw an number of the order of the two consecutive nodes within the
585 * parents way
586 */
587 protected void drawOrderNumber(Point p1, Point p2, int orderNumber) {
588 if (isSegmentVisible(p1, p2) && isLargeSegment(p1, p2, segmentNumberSpace)) {
589 String on = Integer.toString(orderNumber);
590 int strlen = on.length();
591 int x = (p1.x+p2.x)/2 - 4*strlen;
592 int y = (p1.y+p2.y)/2 + 4;
593
594 if(virtualNodeSize != 0 && isLargeSegment(p1, p2, virtualNodeSpace))
595 {
596 y = (p1.y+p2.y)/2 - virtualNodeSize - 3;
597 }
598
599 Color c = g.getColor();
600 g.setColor(backgroundColor);
601 g.fillRect(x-1, y-12, 8*strlen+1, 14);
602 g.setColor(c);
603 g.drawString(on, x, y);
604 }
605 }
606
607 //TODO Not a good place for this method
608 public String getNodeName(Node n) {
609 String name = null;
610 if (n.hasKeys()) {
611 for (String rn : regionalNameOrder) {
612 name = n.get(rn);
613 if (name != null) {
614 break;
615 }
616 }
617 }
618 return name;
619 }
620
621 //TODO Not a good place for this method
622 public String getAreaName(OsmPrimitive w) {
623 String name = null;
624 if (w.hasKeys()) {
625 for (String rn : regionalNameOrder) {
626 name = w.get(rn);
627 if (name != null) {
628 break;
629 }
630 }
631 }
632 return name;
633 }
634
635 public boolean isInactive() {
636 return inactive;
637 }
638
639 public boolean isShowNames() {
640 return showNames;
641 }
642
643 public double getCircum() {
644 return circum;
645 }
646
647 public boolean isShowIcons() {
648 return showIcons;
649 }
650
651}
Note: See TracBrowser for help on using the repository browser.