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

Last change on this file since 1102 was 860, checked in by stoecker, 16 years ago

fixed segment number display

  • Property svn:eol-style set to native
File size: 10.2 KB
Line 
1// License: GPL. See LICENSE file for details.
2
3package org.openstreetmap.josm.gui;
4
5import java.awt.Point;
6import java.util.Collection;
7import java.util.Collections;
8import java.util.HashSet;
9import java.util.TreeMap;
10import java.util.List;
11import java.util.ArrayList;
12import java.util.LinkedList;
13
14import javax.swing.JComponent;
15
16import org.openstreetmap.josm.Main;
17import org.openstreetmap.josm.actions.HelpAction.Helpful;
18import org.openstreetmap.josm.data.coor.EastNorth;
19import org.openstreetmap.josm.data.coor.LatLon;
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.Way;
24import org.openstreetmap.josm.data.osm.WaySegment;
25import org.openstreetmap.josm.data.projection.Projection;
26
27/**
28 * An component that can be navigated by a mapmover. Used as map view and for the
29 * zoomer in the download dialog.
30 *
31 * @author imi
32 */
33public class NavigatableComponent extends JComponent implements Helpful {
34
35 public static final EastNorth world = Main.proj.latlon2eastNorth(new LatLon(Projection.MAX_LAT, Projection.MAX_LON));
36 public static final int snapDistance = sqr(Main.pref.getInteger("node.snap-distance", 10));
37
38 private static int sqr(int a) { return a*a;}
39 /**
40 * The scale factor in x or y-units per pixel. This means, if scale = 10,
41 * every physical pixel on screen are 10 x or 10 y units in the
42 * northing/easting space of the projection.
43 */
44 protected double scale;
45 /**
46 * Center n/e coordinate of the desired screen center.
47 */
48 protected EastNorth center;
49
50 public NavigatableComponent() {
51 setLayout(null);
52 }
53
54 protected DataSet getData()
55 {
56 return Main.ds;
57 }
58
59 /**
60 * Return the OSM-conform zoom factor (0 for whole world, 1 for half, 2 for quarter...)
61 */
62 public int zoom() {
63 double sizex = scale * getWidth();
64 double sizey = scale * getHeight();
65 for (int zoom = 0; zoom <= 32; zoom++, sizex *= 2, sizey *= 2)
66 if (sizex > world.east() || sizey > world.north())
67 return zoom;
68 return 32;
69 }
70
71 /**
72 * Return the current scale value.
73 * @return The scale value currently used in display
74 */
75 public double getScale() {
76 return scale;
77 }
78
79 /**
80 * @return Returns the center point. A copy is returned, so users cannot
81 * change the center by accessing the return value. Use zoomTo instead.
82 */
83 public EastNorth getCenter() {
84 return center;
85 }
86
87 /**
88 * @param x X-Pixelposition to get coordinate from
89 * @param y Y-Pixelposition to get coordinate from
90 *
91 * @return Geographic coordinates from a specific pixel coordination
92 * on the screen.
93 */
94 public EastNorth getEastNorth(int x, int y) {
95 return new EastNorth(
96 center.east() + (x - getWidth()/2.0)*scale,
97 center.north() - (y - getHeight()/2.0)*scale);
98 }
99
100 /**
101 * @param x X-Pixelposition to get coordinate from
102 * @param y Y-Pixelposition to get coordinate from
103 *
104 * @return Geographic unprojected coordinates from a specific pixel coordination
105 * on the screen.
106 */
107 public LatLon getLatLon(int x, int y) {
108
109 return getProjection().eastNorth2latlon(getEastNorth(x, y));
110 }
111
112 /**
113 * Return the point on the screen where this Coordinate would be.
114 * @param p The point, where this geopoint would be drawn.
115 * @return The point on screen where "point" would be drawn, relative
116 * to the own top/left.
117 */
118 public Point getPoint(EastNorth p) {
119 if(null == p)
120 return new Point();
121 double x = (p.east()-center.east())/scale + getWidth()/2;
122 double y = (center.north()-p.north())/scale + getHeight()/2;
123 return new Point((int)x,(int)y);
124 }
125
126 /**
127 * Zoom to the given coordinate.
128 * @param newCenter The center x-value (easting) to zoom to.
129 * @param scale The scale to use.
130 */
131 public void zoomTo(EastNorth newCenter, double scale) {
132 center = newCenter;
133 getProjection().eastNorth2latlon(center);
134 this.scale = scale;
135 repaint();
136 }
137
138 /**
139 * Return the nearest point to the screen point given.
140 * If a node within 10 pixel is found, the nearest node is returned.
141 */
142 public final Node getNearestNode(Point p) {
143 double minDistanceSq = Double.MAX_VALUE;
144 Node minPrimitive = null;
145 for (Node n : getData().nodes) {
146 if (n.deleted || n.incomplete)
147 continue;
148 Point sp = getPoint(n.eastNorth);
149 double dist = p.distanceSq(sp);
150 if (minDistanceSq > dist && dist < snapDistance) {
151 minDistanceSq = p.distanceSq(sp);
152 minPrimitive = n;
153 }
154 // prefer already selected node when multiple nodes on one point
155 else if(minDistanceSq == dist && n.selected && !minPrimitive.selected)
156 {
157 minPrimitive = n;
158 }
159 }
160 return minPrimitive;
161 }
162
163 /**
164 * @return all way segments within 10px of p, sorted by their
165 * perpendicular distance.
166 *
167 * @param p the point for which to search the nearest segment.
168 */
169 public final List<WaySegment> getNearestWaySegments(Point p) {
170 TreeMap<Double, List<WaySegment>> nearest = new TreeMap<Double, List<WaySegment>>();
171 for (Way w : getData().ways) {
172 if (w.deleted || w.incomplete) continue;
173 Node lastN = null;
174 int i = -2;
175 for (Node n : w.nodes) {
176 i++;
177 if (n.deleted || n.incomplete) continue;
178 if (lastN == null) {
179 lastN = n;
180 continue;
181 }
182
183 Point A = getPoint(lastN.eastNorth);
184 Point B = getPoint(n.eastNorth);
185 double c = A.distanceSq(B);
186 double a = p.distanceSq(B);
187 double b = p.distanceSq(A);
188 double perDist = a-(a-b+c)*(a-b+c)/4/c; // perpendicular distance squared
189 if (perDist < snapDistance && a < c+snapDistance && b < c+snapDistance) {
190 if(w.selected) // prefer selected ways a little bit
191 perDist -= 0.00001;
192 List<WaySegment> l;
193 if (nearest.containsKey(perDist)) {
194 l = nearest.get(perDist);
195 } else {
196 l = new LinkedList<WaySegment>();
197 nearest.put(perDist, l);
198 }
199 l.add(new WaySegment(w, i));
200 }
201
202 lastN = n;
203 }
204 }
205 ArrayList<WaySegment> nearestList = new ArrayList<WaySegment>();
206 for (List<WaySegment> wss : nearest.values()) {
207 nearestList.addAll(wss);
208 }
209 return nearestList;
210 }
211
212 /**
213 * @return the nearest way segment to the screen point given that is not
214 * in ignore.
215 *
216 * @param p the point for which to search the nearest segment.
217 * @param ignore a collection of segments which are not to be returned.
218 * May be null.
219 */
220 public final WaySegment getNearestWaySegment(Point p, Collection<WaySegment> ignore) {
221 List<WaySegment> nearest = getNearestWaySegments(p);
222 if (ignore != null) nearest.removeAll(ignore);
223 return nearest.isEmpty() ? null : nearest.get(0);
224 }
225
226 /**
227 * @return the nearest way segment to the screen point given.
228 */
229 public final WaySegment getNearestWaySegment(Point p) {
230 return getNearestWaySegment(p, null);
231 }
232
233 /**
234 * @return the nearest way to the screen point given.
235 */
236 public final Way getNearestWay(Point p) {
237 WaySegment nearestWaySeg = getNearestWaySegment(p);
238 return nearestWaySeg == null ? null : nearestWaySeg.way;
239 }
240
241 /**
242 * Return the object, that is nearest to the given screen point.
243 *
244 * First, a node will be searched. If a node within 10 pixel is found, the
245 * nearest node is returned.
246 *
247 * If no node is found, search for near ways.
248 *
249 * If nothing is found, return <code>null</code>.
250 *
251 * @param p The point on screen.
252 * @return The primitive that is nearest to the point p.
253 */
254 public OsmPrimitive getNearest(Point p) {
255 OsmPrimitive osm = getNearestNode(p);
256 if (osm == null)
257 {
258 osm = getNearestWay(p);
259 }
260 return osm;
261 }
262
263 /**
264 * Returns a singleton of the nearest object, or else an empty collection.
265 */
266 public Collection<OsmPrimitive> getNearestCollection(Point p) {
267 OsmPrimitive osm = getNearest(p);
268 if (osm == null)
269 return Collections.emptySet();
270 return Collections.singleton(osm);
271 }
272
273 /**
274 * @return A list of all objects that are nearest to
275 * the mouse. Does a simple sequential scan on all the data.
276 *
277 * @return A collection of all items or <code>null</code>
278 * if no item under or near the point. The returned
279 * list is never empty.
280 */
281 public Collection<OsmPrimitive> getAllNearest(Point p) {
282 Collection<OsmPrimitive> nearest = new HashSet<OsmPrimitive>();
283 for (Way w : getData().ways) {
284 if (w.deleted || w.incomplete) continue;
285 Node lastN = null;
286 for (Node n : w.nodes) {
287 if (n.deleted || n.incomplete) continue;
288 if (lastN == null) {
289 lastN = n;
290 continue;
291 }
292 Point A = getPoint(lastN.eastNorth);
293 Point B = getPoint(n.eastNorth);
294 double c = A.distanceSq(B);
295 double a = p.distanceSq(B);
296 double b = p.distanceSq(A);
297 double perDist = a-(a-b+c)*(a-b+c)/4/c; // perpendicular distance squared
298 if (perDist < snapDistance && a < c+snapDistance && b < c+snapDistance) {
299 nearest.add(w);
300 break;
301 }
302 lastN = n;
303 }
304 }
305 for (Node n : getData().nodes) {
306 if (!n.deleted && !n.incomplete
307 && getPoint(n.eastNorth).distanceSq(p) < snapDistance) {
308 nearest.add(n);
309 }
310 }
311 return nearest.isEmpty() ? null : nearest;
312 }
313
314 /**
315 * @return A list of all nodes that are nearest to
316 * the mouse. Does a simple sequential scan on all the data.
317 *
318 * @return A collection of all nodes or <code>null</code>
319 * if no node under or near the point. The returned
320 * list is never empty.
321 */
322 public Collection<Node> getNearestNodes(Point p) {
323 Collection<Node> nearest = new HashSet<Node>();
324 for (Node n : getData().nodes) {
325 if (!n.deleted && !n.incomplete
326 && getPoint(n.eastNorth).distanceSq(p) < snapDistance) {
327 nearest.add(n);
328 }
329 }
330 return nearest.isEmpty() ? null : nearest;
331 }
332
333 /**
334 * @return the nearest nodes to the screen point given that is not
335 * in ignore.
336 *
337 * @param p the point for which to search the nearest segment.
338 * @param ignore a collection of nodes which are not to be returned.
339 * May be null.
340 */
341 public final Collection<Node> getNearestNodes(Point p, Collection<Node> ignore) {
342 Collection<Node> nearest = getNearestNodes(p);
343 if (nearest == null) return null;
344 if (ignore != null) nearest.removeAll(ignore);
345 return nearest.isEmpty() ? null : nearest;
346 }
347
348 /**
349 * @return The projection to be used in calculating stuff.
350 */
351 protected Projection getProjection() {
352 return Main.proj;
353 }
354
355 public String helpTopic() {
356 String n = getClass().getName();
357 return n.substring(n.lastIndexOf('.')+1);
358 }
359}
Note: See TracBrowser for help on using the repository browser.