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

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