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

Last change on this file since 729 was 655, checked in by ramack, 16 years ago

patch by bruce89, closes #812; thanks bruce

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