source: josm/trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java@ 3351

Last change on this file since 3351 was 3177, checked in by bastiK, 14 years ago

Filter: improved selection handling. (Don't allow to select filtered or disabled objects by clicking on them. Don't connect to hidden ways in add mode.)

  • Property svn:eol-style set to native
File size: 24.8 KB
Line 
1// License: GPL. See LICENSE file for details.
2
3package org.openstreetmap.josm.gui;
4
5import java.awt.Point;
6import java.awt.Rectangle;
7import java.util.ArrayList;
8import java.util.Collection;
9import java.util.Collections;
10import java.util.Date;
11import java.util.HashSet;
12import java.util.LinkedList;
13import java.util.List;
14import java.util.Stack;
15import java.util.TreeMap;
16import java.util.concurrent.CopyOnWriteArrayList;
17
18import javax.swing.JComponent;
19
20import org.openstreetmap.josm.Main;
21import org.openstreetmap.josm.data.Bounds;
22import org.openstreetmap.josm.data.ProjectionBounds;
23import org.openstreetmap.josm.data.coor.CachedLatLon;
24import org.openstreetmap.josm.data.coor.EastNorth;
25import org.openstreetmap.josm.data.coor.LatLon;
26import org.openstreetmap.josm.data.osm.BBox;
27import org.openstreetmap.josm.data.osm.DataSet;
28import org.openstreetmap.josm.data.osm.Node;
29import org.openstreetmap.josm.data.osm.OsmPrimitive;
30import org.openstreetmap.josm.data.osm.Way;
31import org.openstreetmap.josm.data.osm.WaySegment;
32import org.openstreetmap.josm.data.projection.Projection;
33import org.openstreetmap.josm.gui.help.Helpful;
34import org.openstreetmap.josm.tools.Predicate;
35
36/**
37 * An component that can be navigated by a mapmover. Used as map view and for the
38 * zoomer in the download dialog.
39 *
40 * @author imi
41 */
42public class NavigatableComponent extends JComponent implements Helpful {
43
44 /**
45 * Interface to notify listeners of the change of the zoom area.
46 */
47 public interface ZoomChangeListener {
48 void zoomChanged();
49 }
50
51 /**
52 * the zoom listeners
53 */
54 private static final CopyOnWriteArrayList<ZoomChangeListener> zoomChangeListeners = new CopyOnWriteArrayList<ZoomChangeListener>();
55
56 /**
57 * Removes a zoom change listener
58 *
59 * @param listener the listener. Ignored if null or already absent
60 */
61 public static void removeZoomChangeListener(NavigatableComponent.ZoomChangeListener listener) {
62 zoomChangeListeners.remove(listener);
63 }
64
65 /**
66 * Adds a zoom change listener
67 *
68 * @param listener the listener. Ignored if null or already registered.
69 */
70 public static void addZoomChangeListener(NavigatableComponent.ZoomChangeListener listener) {
71 if (listener != null) {
72 zoomChangeListeners.addIfAbsent(listener);
73 }
74 }
75
76 protected static void fireZoomChanged() {
77 for (ZoomChangeListener l : zoomChangeListeners) {
78 l.zoomChanged();
79 }
80 }
81
82 public static final int snapDistance = Main.pref.getInteger("node.snap-distance", 10);
83 public static final int snapDistanceSq = sqr(snapDistance);
84
85 private static int sqr(int a) { return a*a;}
86 /**
87 * The scale factor in x or y-units per pixel. This means, if scale = 10,
88 * every physical pixel on screen are 10 x or 10 y units in the
89 * northing/easting space of the projection.
90 */
91 private double scale = Main.proj.getDefaultZoomInPPD();
92 /**
93 * Center n/e coordinate of the desired screen center.
94 */
95 protected EastNorth center = calculateDefaultCenter();
96
97 public NavigatableComponent() {
98 setLayout(null);
99 }
100
101 protected DataSet getCurrentDataSet() {
102 return Main.main.getCurrentDataSet();
103 }
104
105 private EastNorth calculateDefaultCenter() {
106 Bounds b = Main.proj.getWorldBoundsLatLon();
107 double lat = (b.getMax().lat() + b.getMin().lat())/2;
108 double lon = (b.getMax().lon() + b.getMin().lon())/2;
109
110 return Main.proj.latlon2eastNorth(new LatLon(lat, lon));
111 }
112
113 public String getDist100PixelText()
114 {
115 double dist = getDist100Pixel();
116 return dist >= 2000 ? Math.round(dist/100)/10 +" km" : (dist >= 1
117 ? Math.round(dist*10)/10 +" m" : "< 1 m");
118 }
119
120 public double getDist100Pixel()
121 {
122 int w = getWidth()/2;
123 int h = getHeight()/2;
124 LatLon ll1 = getLatLon(w-50,h);
125 LatLon ll2 = getLatLon(w+50,h);
126 return ll1.greatCircleDistance(ll2);
127 }
128
129 /**
130 * @return Returns the center point. A copy is returned, so users cannot
131 * change the center by accessing the return value. Use zoomTo instead.
132 */
133 public EastNorth getCenter() {
134 return center;
135 }
136
137 /**
138 * @param x X-Pixelposition to get coordinate from
139 * @param y Y-Pixelposition to get coordinate from
140 *
141 * @return Geographic coordinates from a specific pixel coordination
142 * on the screen.
143 */
144 public EastNorth getEastNorth(int x, int y) {
145 return new EastNorth(
146 center.east() + (x - getWidth()/2.0)*scale,
147 center.north() - (y - getHeight()/2.0)*scale);
148 }
149
150 public ProjectionBounds getProjectionBounds() {
151 return new ProjectionBounds(
152 new EastNorth(
153 center.east() - getWidth()/2.0*scale,
154 center.north() - getHeight()/2.0*scale),
155 new EastNorth(
156 center.east() + getWidth()/2.0*scale,
157 center.north() + getHeight()/2.0*scale));
158 }
159
160 /* FIXME: replace with better method - used by MapSlider */
161 public ProjectionBounds getMaxProjectionBounds() {
162 Bounds b = getProjection().getWorldBoundsLatLon();
163 return new ProjectionBounds(getProjection().latlon2eastNorth(b.getMin()),
164 getProjection().latlon2eastNorth(b.getMax()));
165 }
166
167 /* FIXME: replace with better method - used by Main to reset Bounds when projection changes, don't use otherwise */
168 public Bounds getRealBounds() {
169 return new Bounds(
170 getProjection().eastNorth2latlon(new EastNorth(
171 center.east() - getWidth()/2.0*scale,
172 center.north() - getHeight()/2.0*scale)),
173 getProjection().eastNorth2latlon(new EastNorth(
174 center.east() + getWidth()/2.0*scale,
175 center.north() + getHeight()/2.0*scale)));
176 }
177
178 /**
179 * @param x X-Pixelposition to get coordinate from
180 * @param y Y-Pixelposition to get coordinate from
181 *
182 * @return Geographic unprojected coordinates from a specific pixel coordination
183 * on the screen.
184 */
185 public LatLon getLatLon(int x, int y) {
186 return getProjection().eastNorth2latlon(getEastNorth(x, y));
187 }
188
189 /**
190 * @param r
191 * @return Minimum bounds that will cover rectangle
192 */
193 public Bounds getLatLonBounds(Rectangle r) {
194 // TODO Maybe this should be (optional) method of Projection implementation
195 EastNorth p1 = getEastNorth(r.x, r.y);
196 EastNorth p2 = getEastNorth(r.x + r.width, r.y + r.height);
197
198 Bounds result = new Bounds(Main.proj.eastNorth2latlon(p1));
199
200 double eastMin = Math.min(p1.east(), p2.east());
201 double eastMax = Math.max(p1.east(), p2.east());
202 double northMin = Math.min(p1.north(), p2.north());
203 double northMax = Math.max(p1.north(), p2.north());
204 double deltaEast = (eastMax - eastMin) / 10;
205 double deltaNorth = (northMax - northMin) / 10;
206
207 for (int i=0; i < 10; i++) {
208 result.extend(Main.proj.eastNorth2latlon(new EastNorth(eastMin + i * deltaEast, northMin)));
209 result.extend(Main.proj.eastNorth2latlon(new EastNorth(eastMin + i * deltaEast, northMax)));
210 result.extend(Main.proj.eastNorth2latlon(new EastNorth(eastMin, northMin + i * deltaNorth)));
211 result.extend(Main.proj.eastNorth2latlon(new EastNorth(eastMax, northMin + i * deltaNorth)));
212 }
213
214 return result;
215 }
216
217 /**
218 * Return the point on the screen where this Coordinate would be.
219 * @param p The point, where this geopoint would be drawn.
220 * @return The point on screen where "point" would be drawn, relative
221 * to the own top/left.
222 */
223 public Point getPoint(EastNorth p) {
224 if (null == p)
225 return new Point();
226 double x = (p.east()-center.east())/scale + getWidth()/2;
227 double y = (center.north()-p.north())/scale + getHeight()/2;
228 return new Point((int)x,(int)y);
229 }
230
231 public Point getPoint(LatLon latlon) {
232 if (latlon == null)
233 return new Point();
234 else if (latlon instanceof CachedLatLon)
235 return getPoint(((CachedLatLon)latlon).getEastNorth());
236 else
237 return getPoint(getProjection().latlon2eastNorth(latlon));
238 }
239 public Point getPoint(Node n) {
240 return getPoint(n.getEastNorth());
241 }
242
243 /**
244 * Zoom to the given coordinate.
245 * @param newCenter The center x-value (easting) to zoom to.
246 * @param scale The scale to use.
247 */
248 private void zoomTo(EastNorth newCenter, double newScale) {
249 Bounds b = getProjection().getWorldBoundsLatLon();
250 CachedLatLon cl = new CachedLatLon(newCenter);
251 boolean changed = false;
252 double lat = cl.lat();
253 double lon = cl.lon();
254 if(lat < b.getMin().lat()) {changed = true; lat = b.getMin().lat(); }
255 else if(lat > b.getMax().lat()) {changed = true; lat = b.getMax().lat(); }
256 if(lon < b.getMin().lon()) {changed = true; lon = b.getMin().lon(); }
257 else if(lon > b.getMax().lon()) {changed = true; lon = b.getMax().lon(); }
258 if(changed) {
259 newCenter = new CachedLatLon(lat, lon).getEastNorth();
260 }
261 int width = getWidth()/2;
262 int height = getHeight()/2;
263 LatLon l1 = new LatLon(b.getMin().lat(), lon);
264 LatLon l2 = new LatLon(b.getMax().lat(), lon);
265 EastNorth e1 = getProjection().latlon2eastNorth(l1);
266 EastNorth e2 = getProjection().latlon2eastNorth(l2);
267 double d = e2.north() - e1.north();
268 if(d < height*newScale)
269 {
270 double newScaleH = d/height;
271 e1 = getProjection().latlon2eastNorth(new LatLon(lat, b.getMin().lon()));
272 e2 = getProjection().latlon2eastNorth(new LatLon(lat, b.getMax().lon()));
273 d = e2.east() - e1.east();
274 if(d < width*newScale) {
275 newScale = Math.max(newScaleH, d/width);
276 }
277 }
278 else
279 {
280 d = d/(l1.greatCircleDistance(l2)*height*10);
281 if(newScale < d) {
282 newScale = d;
283 }
284 }
285
286 if (!newCenter.equals(center) || (scale != newScale)) {
287 pushZoomUndo(center, scale);
288 zoomNoUndoTo(newCenter, newScale);
289 }
290 }
291
292 /**
293 * Zoom to the given coordinate without adding to the zoom undo buffer.
294 * @param newCenter The center x-value (easting) to zoom to.
295 * @param scale The scale to use.
296 */
297 private void zoomNoUndoTo(EastNorth newCenter, double newScale) {
298 if (!newCenter.equals(center)) {
299 EastNorth oldCenter = center;
300 center = newCenter;
301 firePropertyChange("center", oldCenter, newCenter);
302 }
303 if (scale != newScale) {
304 double oldScale = scale;
305 scale = newScale;
306 firePropertyChange("scale", oldScale, newScale);
307 }
308
309 repaint();
310 fireZoomChanged();
311 }
312
313 public void zoomTo(EastNorth newCenter) {
314 zoomTo(newCenter, scale);
315 }
316
317 public void zoomTo(LatLon newCenter) {
318 if(newCenter instanceof CachedLatLon) {
319 zoomTo(((CachedLatLon)newCenter).getEastNorth(), scale);
320 } else {
321 zoomTo(getProjection().latlon2eastNorth(newCenter), scale);
322 }
323 }
324
325 public void zoomToFactor(double x, double y, double factor) {
326 double newScale = scale*factor;
327 // New center position so that point under the mouse pointer stays the same place as it was before zooming
328 // You will get the formula by simplifying this expression: newCenter = oldCenter + mouseCoordinatesInNewZoom - mouseCoordinatesInOldZoom
329 zoomTo(new EastNorth(
330 center.east() - (x - getWidth()/2.0) * (newScale - scale),
331 center.north() + (y - getHeight()/2.0) * (newScale - scale)),
332 newScale);
333 }
334
335 public void zoomToFactor(EastNorth newCenter, double factor) {
336 zoomTo(newCenter, scale*factor);
337 }
338
339 public void zoomToFactor(double factor) {
340 zoomTo(center, scale*factor);
341 }
342
343 public void zoomTo(ProjectionBounds box) {
344 // -20 to leave some border
345 int w = getWidth()-20;
346 if (w < 20) {
347 w = 20;
348 }
349 int h = getHeight()-20;
350 if (h < 20) {
351 h = 20;
352 }
353
354 double scaleX = (box.max.east()-box.min.east())/w;
355 double scaleY = (box.max.north()-box.min.north())/h;
356 double newScale = Math.max(scaleX, scaleY);
357
358 zoomTo(box.getCenter(), newScale);
359 }
360
361 public void zoomTo(Bounds box) {
362 zoomTo(new ProjectionBounds(getProjection().latlon2eastNorth(box.getMin()),
363 getProjection().latlon2eastNorth(box.getMax())));
364 }
365
366 private class ZoomData {
367 LatLon center;
368 double scale;
369
370 public ZoomData(EastNorth center, double scale) {
371 this.center = new CachedLatLon(center);
372 this.scale = scale;
373 }
374
375 public EastNorth getCenterEastNorth() {
376 return getProjection().latlon2eastNorth(center);
377 }
378
379 public double getScale() {
380 return scale;
381 }
382 }
383
384 private Stack<ZoomData> zoomUndoBuffer = new Stack<ZoomData>();
385 private Stack<ZoomData> zoomRedoBuffer = new Stack<ZoomData>();
386 private Date zoomTimestamp = new Date();
387
388 private void pushZoomUndo(EastNorth center, double scale) {
389 Date now = new Date();
390 if ((now.getTime() - zoomTimestamp.getTime()) > (Main.pref.getDouble("zoom.undo.delay", 1.0) * 1000)) {
391 zoomUndoBuffer.push(new ZoomData(center, scale));
392 if (zoomUndoBuffer.size() > Main.pref.getInteger("zoom.undo.max", 50)) {
393 zoomUndoBuffer.remove(0);
394 }
395 zoomRedoBuffer.clear();
396 }
397 zoomTimestamp = now;
398 }
399
400 public void zoomPrevious() {
401 if (!zoomUndoBuffer.isEmpty()) {
402 ZoomData zoom = zoomUndoBuffer.pop();
403 zoomRedoBuffer.push(new ZoomData(center, scale));
404 zoomNoUndoTo(zoom.getCenterEastNorth(), zoom.getScale());
405 }
406 }
407
408 public void zoomNext() {
409 if (!zoomRedoBuffer.isEmpty()) {
410 ZoomData zoom = zoomRedoBuffer.pop();
411 zoomUndoBuffer.push(new ZoomData(center, scale));
412 zoomNoUndoTo(zoom.getCenterEastNorth(), zoom.getScale());
413 }
414 }
415
416 public boolean hasZoomUndoEntries() {
417 return !zoomUndoBuffer.isEmpty();
418 }
419
420 public boolean hasZoomRedoEntries() {
421 return !zoomRedoBuffer.isEmpty();
422 }
423
424 private BBox getSnapDistanceBBox(Point p) {
425 return new BBox(getLatLon(p.x - snapDistance, p.y - snapDistance),
426 getLatLon(p.x + snapDistance, p.y + snapDistance));
427 }
428
429 @Deprecated
430 public final Node getNearestNode(Point p) {
431 return getNearestNode(p, OsmPrimitive.isUsablePredicate);
432 }
433
434 /**
435 * Return the nearest node to the screen point given.
436 * If more then one node within snapDistance pixel is found,
437 * the nearest node is returned.
438 * @param p the screen point
439 * @param predicate this parameter imposes a condition on the returned object, e.g.
440 * give the nearest node that is tagged.
441 */
442 public final Node getNearestNode(Point p, Predicate<OsmPrimitive> predicate) {
443 DataSet ds = getCurrentDataSet();
444 if (ds == null)
445 return null;
446
447 double minDistanceSq = snapDistanceSq;
448 Node minPrimitive = null;
449 for (Node n : ds.searchNodes(getSnapDistanceBBox(p))) {
450 if (! predicate.evaluate(n))
451 continue;
452 Point sp = getPoint(n);
453 double dist = p.distanceSq(sp);
454 if (dist < minDistanceSq) {
455 minDistanceSq = dist;
456 minPrimitive = n;
457 }
458 // when multiple nodes on one point, prefer new or selected nodes
459 else if (dist == minDistanceSq && minPrimitive != null
460 && ((n.isNew() && ds.isSelected(n))
461 || (!ds.isSelected(minPrimitive) && (ds.isSelected(n) || n.isNew())))) {
462 minPrimitive = n;
463 }
464 }
465 return minPrimitive;
466 }
467
468 /**
469 * @return all way segments within 10px of p, sorted by their
470 * perpendicular distance.
471 *
472 * @param p the point for which to search the nearest segment.
473 * @param predicate the returned objects have to fulfill certain properties.
474 */
475 public final List<WaySegment> getNearestWaySegments(Point p, Predicate<OsmPrimitive> predicate) {
476 TreeMap<Double, List<WaySegment>> nearest = new TreeMap<Double, List<WaySegment>>();
477 DataSet ds = getCurrentDataSet();
478 if (ds == null)
479 return null;
480
481 for (Way w : ds.searchWays(getSnapDistanceBBox(p))) {
482 if (!predicate.evaluate(w))
483 continue;
484 Node lastN = null;
485 int i = -2;
486 for (Node n : w.getNodes()) {
487 i++;
488 if (n.isDeleted() || n.isIncomplete()) {//FIXME: This shouldn't happen, raise exception?
489 continue;
490 }
491 if (lastN == null) {
492 lastN = n;
493 continue;
494 }
495
496 Point A = getPoint(lastN);
497 Point B = getPoint(n);
498 double c = A.distanceSq(B);
499 double a = p.distanceSq(B);
500 double b = p.distanceSq(A);
501 double perDist = a - (a - b + c) * (a - b + c) / 4 / c; // perpendicular distance squared
502 if (perDist < snapDistanceSq && a < c + snapDistanceSq && b < c + snapDistanceSq) {
503 if (ds.isSelected(w)) {
504 perDist -= 0.00001;
505 }
506 List<WaySegment> l;
507 if (nearest.containsKey(perDist)) {
508 l = nearest.get(perDist);
509 } else {
510 l = new LinkedList<WaySegment>();
511 nearest.put(perDist, l);
512 }
513 l.add(new WaySegment(w, i));
514 }
515
516 lastN = n;
517 }
518 }
519 ArrayList<WaySegment> nearestList = new ArrayList<WaySegment>();
520 for (List<WaySegment> wss : nearest.values()) {
521 nearestList.addAll(wss);
522 }
523 return nearestList;
524 }
525
526 /**
527 * @return the nearest way segment to the screen point given that is not
528 * in ignore.
529 *
530 * @param p the point for which to search the nearest segment.
531 * @param ignore a collection of segments which are not to be returned.
532 * @param predicate the returned object has to fulfill certain properties.
533 * May be null.
534 */
535 public final WaySegment getNearestWaySegment
536 (Point p, Collection<WaySegment> ignore, Predicate<OsmPrimitive> predicate) {
537 List<WaySegment> nearest = getNearestWaySegments(p, predicate);
538 if(nearest == null)
539 return null;
540 if (ignore != null) {
541 nearest.removeAll(ignore);
542 }
543 return nearest.isEmpty() ? null : nearest.get(0);
544 }
545
546 /**
547 * @return the nearest way segment to the screen point given.
548 */
549 public final WaySegment getNearestWaySegment(Point p, Predicate<OsmPrimitive> predicate) {
550 return getNearestWaySegment(p, null, predicate);
551 }
552
553 @Deprecated
554 public final Way getNearestWay(Point p) {
555 return getNearestWay(p, OsmPrimitive.isUsablePredicate);
556 }
557
558 /**
559 * @return the nearest way to the screen point given.
560 */
561 public final Way getNearestWay(Point p, Predicate<OsmPrimitive> predicate) {
562 WaySegment nearestWaySeg = getNearestWaySegment(p, predicate);
563 return nearestWaySeg == null ? null : nearestWaySeg.way;
564 }
565
566 /**
567 * Return the object, that is nearest to the given screen point.
568 *
569 * First, a node will be searched. If a node within 10 pixel is found, the
570 * nearest node is returned.
571 *
572 * If no node is found, search for near ways.
573 *
574 * If nothing is found, return <code>null</code>.
575 *
576 * @param p The point on screen.
577 * @param predicate the returned object has to fulfill certain properties.
578 * @return The primitive that is nearest to the point p.
579 */
580 public OsmPrimitive getNearest(Point p, Predicate<OsmPrimitive> predicate) {
581 OsmPrimitive osm = getNearestNode(p, predicate);
582 if (osm == null)
583 {
584 osm = getNearestWay(p, predicate);
585 }
586 return osm;
587 }
588
589 /**
590 * Returns a singleton of the nearest object, or else an empty collection.
591 */
592 public Collection<OsmPrimitive> getNearestCollection(Point p, Predicate<OsmPrimitive> predicate) {
593 OsmPrimitive osm = getNearest(p, predicate);
594 if (osm == null)
595 return Collections.emptySet();
596 return Collections.singleton(osm);
597 }
598
599 /**
600 * @return A list of all objects that are nearest to
601 * the mouse.
602 *
603 * @return A collection of all items or <code>null</code>
604 * if no item under or near the point. The returned
605 * list is never empty.
606 */
607 public Collection<OsmPrimitive> getAllNearest(Point p, Predicate<OsmPrimitive> predicate) {
608 Collection<OsmPrimitive> nearest = new HashSet<OsmPrimitive>();
609 DataSet ds = getCurrentDataSet();
610 if (ds == null)
611 return null;
612 for (Way w : ds.searchWays(getSnapDistanceBBox(p))) {
613 if (!predicate.evaluate(w))
614 continue;
615 Node lastN = null;
616 for (Node n : w.getNodes()) {
617 if (!predicate.evaluate(n))
618 continue;
619 if (lastN == null) {
620 lastN = n;
621 continue;
622 }
623 Point A = getPoint(lastN);
624 Point B = getPoint(n);
625 double c = A.distanceSq(B);
626 double a = p.distanceSq(B);
627 double b = p.distanceSq(A);
628 double perDist = a - (a - b + c) * (a - b + c) / 4 / c; // perpendicular distance squared
629 if (perDist < snapDistanceSq && a < c + snapDistanceSq && b < c + snapDistanceSq) {
630 nearest.add(w);
631 break;
632 }
633 lastN = n;
634 }
635 }
636 for (Node n : ds.searchNodes(getSnapDistanceBBox(p))) {
637 if (n.isUsable()
638 && getPoint(n).distanceSq(p) < snapDistanceSq) {
639 nearest.add(n);
640 }
641 }
642 return nearest.isEmpty() ? null : nearest;
643 }
644
645 /**
646 * @return A list of all nodes that are nearest to
647 * the mouse.
648 *
649 * @return A collection of all nodes or <code>null</code>
650 * if no node under or near the point. The returned
651 * list is never empty.
652 */
653 public Collection<Node> getNearestNodes(Point p, Predicate<OsmPrimitive> predicate) {
654 Collection<Node> nearest = new HashSet<Node>();
655 DataSet ds = getCurrentDataSet();
656 if (ds == null)
657 return null;
658
659 for (Node n : ds.searchNodes(getSnapDistanceBBox(p))) {
660 if (!predicate.evaluate(n))
661 continue;
662 if (getPoint(n).distanceSq(p) < snapDistanceSq) {
663 nearest.add(n);
664 }
665 }
666 return nearest.isEmpty() ? null : nearest;
667 }
668
669 /**
670 * @return the nearest nodes to the screen point given that is not
671 * in ignore.
672 *
673 * @param p the point for which to search the nearest segment.
674 * @param ignore a collection of nodes which are not to be returned.
675 * @param predicate the returned objects have to fulfill certain properties.
676 * May be null.
677 */
678 public final Collection<Node> getNearestNodes(Point p, Collection<Node> ignore, Predicate<OsmPrimitive> predicate) {
679 Collection<Node> nearest = getNearestNodes(p, predicate);
680 if (nearest == null) return null;
681 if (ignore != null) {
682 nearest.removeAll(ignore);
683 }
684 return nearest.isEmpty() ? null : nearest;
685 }
686
687 /**
688 * @return The projection to be used in calculating stuff.
689 */
690 public Projection getProjection() {
691 return Main.proj;
692 }
693
694 public String helpTopic() {
695 String n = getClass().getName();
696 return n.substring(n.lastIndexOf('.')+1);
697 }
698
699 /**
700 * Return a ID which is unique as long as viewport dimensions are the same
701 */
702 public int getViewID() {
703 String x = center.east() + "_" + center.north() + "_" + scale + "_" +
704 getWidth() + "_" + getHeight() + "_" + getProjection().toString();
705 java.util.zip.CRC32 id = new java.util.zip.CRC32();
706 id.update(x.getBytes());
707 return (int)id.getValue();
708 }
709}
Note: See TracBrowser for help on using the repository browser.