source: josm/src/org/openstreetmap/josm/gui/MapView.java@ 4

Last change on this file since 4 was 4, checked in by imi, 19 years ago

improved selection

File size: 9.2 KB
Line 
1package org.openstreetmap.josm.gui;
2
3import java.awt.Color;
4import java.awt.Graphics;
5import java.awt.Point;
6import java.awt.event.ActionEvent;
7import java.awt.event.ComponentEvent;
8import java.awt.event.ComponentListener;
9import java.awt.event.KeyEvent;
10import java.util.LinkedList;
11import java.util.List;
12
13import javax.swing.AbstractAction;
14import javax.swing.ImageIcon;
15import javax.swing.JComponent;
16import javax.swing.event.ChangeEvent;
17import javax.swing.event.ChangeListener;
18
19import org.openstreetmap.josm.data.Bounds;
20import org.openstreetmap.josm.data.GeoPoint;
21import org.openstreetmap.josm.data.osm.DataSet;
22import org.openstreetmap.josm.data.osm.LineSegment;
23import org.openstreetmap.josm.data.osm.Node;
24import org.openstreetmap.josm.data.osm.Track;
25import org.openstreetmap.josm.data.projection.Projection;
26
27/**
28 * This is a component used in the MapFrame for browsing the map. It use is to
29 * provide the MapMode's enough capabilities to operate.
30 *
31 * MapView holds the map data, organize it, convert it, provide access to it.
32 *
33 * MapView hold meta-data about the data set currently displayed, as scale level,
34 * center point viewed, what scrolling mode or editing mode is selected or with
35 * what projection the map is viewed etc..
36 *
37 * @author imi
38 */
39public class MapView extends JComponent implements ComponentListener, ChangeListener {
40
41 /**
42 * Toggles the autoScale feature of the mapView
43 * @author imi
44 */
45 public class AutoScaleAction extends AbstractAction {
46 public AutoScaleAction() {
47 super("Auto Scale", new ImageIcon("images/autoscale.png"));
48 putValue(MNEMONIC_KEY, KeyEvent.VK_A);
49 }
50 public void actionPerformed(ActionEvent e) {
51 autoScale = !autoScale;
52 recalculateCenterScale();
53 }
54 }
55
56 /**
57 * Whether to adjust the scale property on every resize.
58 */
59 private boolean autoScale = true;
60
61 /**
62 * The scale factor in meter per pixel.
63 */
64 private double scale;
65 /**
66 * Center n/e coordinate of the desired screen center.
67 */
68 private GeoPoint center;
69
70 /**
71 * Used projection method in this map
72 */
73 private Projection projection = Main.pref.projection.clone();
74
75 /**
76 * The underlying DataSet.
77 */
78 public final DataSet dataSet;
79
80
81 // Event handling functions and data
82
83 /**
84 * The event list with all state chaned listener
85 */
86 List<ChangeListener> listener = new LinkedList<ChangeListener>();
87 /**
88 * Add an event listener to the state changed event queue. If passed
89 * <code>null</code>, nothing happens.
90 */
91 public final void addChangeListener(ChangeListener l) {
92 if (l != null)
93 listener.add(l);
94 }
95 /**
96 * Remove an event listener from the event queue. If passed
97 * <code>null</code>, nothing happens.
98 */
99 public final void removeChangeListener(ChangeListener l) {
100 listener.remove(l);
101 }
102 /**
103 * Fires an ChangeEvent. Occours, when an non-data value changed, as example
104 * the autoScale - state or the center. Is not fired, dataSet internal things
105 * changes.
106 */
107 public final void fireStateChanged() {
108 ChangeEvent e = null;
109 for(ChangeListener l : listener) {
110 if (e == null)
111 e = new ChangeEvent(this);
112 l.stateChanged(e);
113 }
114 }
115
116
117
118
119
120 // other functions
121
122 /**
123 * Construct a MapView and attach it to a frame.
124 */
125 public MapView(DataSet dataSet) {
126 this.dataSet = dataSet;
127 addComponentListener(this);
128
129 // initialize the movement listener
130 new MapMover(this);
131
132 // initialize the projection
133 setProjection(Main.pref.projection.clone());
134 }
135
136 /**
137 * Get geographic coordinates from a specific pixel coordination
138 * on the screen.
139 *
140 * If you don't need it, provide false at third parameter to speed
141 * up the calculation.
142 *
143 * @param x X-Pixelposition to get coordinate from
144 * @param y Y-Pixelposition to get coordinate from
145 * @param latlon If set, the return value will also have the
146 * latitude/longitude filled.
147 *
148 * @return The geographic coordinate, filled with x/y (northing/easting)
149 * settings and, if requested with latitude/longitude.
150 */
151 public GeoPoint getPoint(int x, int y, boolean latlon) {
152 GeoPoint p = new GeoPoint();
153 p.x = (x - getWidth()/2.0)*scale + center.x;
154 p.y = (getHeight()/2.0 - y)*scale + center.y;
155 if (latlon)
156 getProjection().xy2latlon(p);
157 return p;
158 }
159
160 /**
161 * Return the point on the screen where this GeoPoint would be.
162 * @param point The point, where this geopoint would be drawn.
163 * @return The point on screen where "point" would be drawn, relative
164 * to the own top/left.
165 */
166 public Point getScreenPoint(GeoPoint point) {
167 GeoPoint p;
168 if (!Double.isNaN(point.x) && !Double.isNaN(point.y))
169 p = point;
170 else {
171 if (Double.isNaN(point.lat) || Double.isNaN(point.lon))
172 throw new IllegalArgumentException("point: Either lat/lon or x/y must be set.");
173 p = point.clone();
174 projection.latlon2xy(p);
175 }
176 return new Point(toScreenX(p.x), toScreenY(p.y));
177 }
178
179 /**
180 * Zoom to the given coordinate.
181 * @param centerX The center x-value (easting) to zoom to.
182 * @param centerY The center y-value (northing) to zoom to.
183 * @param scale The scale to use.
184 */
185 public void zoomTo(GeoPoint newCenter, double scale) {
186 autoScale = false;
187 center = newCenter.clone();
188 projection.xy2latlon(center);
189 this.scale = scale;
190 recalculateCenterScale();
191 fireStateChanged(); // in case of autoScale, recalculate fired.
192 }
193
194 /**
195 * Return the current scale value.
196 * @return The scale value currently used in display
197 */
198 public double getScale() {
199 return scale;
200 }
201
202 /**
203 * Set the new dimension to the projection class. Also adjust the components
204 * scale, if in autoScale mode.
205 */
206 private void recalculateCenterScale() {
207 if (autoScale) {
208 // -20 to leave some border
209 int w = getWidth()-20;
210 if (w < 20)
211 w = 20;
212 int h = getHeight()-20;
213 if (h < 20)
214 h = 20;
215 Bounds bounds = dataSet.getBoundsXY();
216 if (bounds == null) {
217 // no bounds means standard scale and center
218 center = new GeoPoint(51.526447, -0.14746371);
219 getProjection().latlon2xy(center);
220 scale = 10000; // nice view from 1:10000, eh?
221 } else {
222 center = bounds.centerXY();
223 getProjection().xy2latlon(center);
224 double scaleX = (bounds.max.x-bounds.min.x)/w;
225 double scaleY = (bounds.max.y-bounds.min.y)/h;
226 scale = Math.max(scaleX, scaleY); // minimum scale to see all of the screen
227 }
228 fireStateChanged();
229 }
230 repaint();
231 }
232
233 /**
234 * Call to recalculateCenterScale.
235 */
236 public void componentResized(ComponentEvent e) {
237 recalculateCenterScale();
238 }
239
240 /**
241 * Draw the component.
242 */
243 @Override
244 public void paint(Graphics g) {
245 // empty out everything
246 g.setColor(Color.BLACK);
247 g.fillRect(0,0,getWidth(),getHeight());
248
249 // draw tracks
250 if (dataSet.tracks != null)
251 for (Track track : dataSet.tracks)
252 for (LineSegment ls : track.segments) {
253 g.setColor(ls.selected || track.selected ? Color.WHITE : Color.GRAY);
254 g.drawLine(toScreenX(ls.start.coor.x), toScreenY(ls.start.coor.y),
255 toScreenX(ls.end.coor.x), toScreenY(ls.end.coor.y));
256 }
257
258 // draw nodes
259 for (Node w : dataSet.allNodes) {
260 g.setColor(w.selected ? Color.WHITE : Color.RED);
261 g.drawArc(toScreenX(w.coor.x), toScreenY(w.coor.y), 3, 3, 0, 360);
262 }
263 }
264
265 /**
266 * Return the x-screen coordinate for the given point.
267 * @param x The X-position (easting) of the desired point
268 * @return The screen coordinate for the point.
269 */
270 public int toScreenX(double x) {
271 return (int)Math.round((x-center.x) / scale + getWidth()/2);
272 }
273
274 /**
275 * Return the y-screen coordinate for the given point.
276 * @param y The Y-position (northing) of the desired point
277 * @return The screen coordinate for the point.
278 */
279 public int toScreenY(double y) {
280 return (int)Math.round((center.y-y) / scale + getHeight()/2);
281 }
282
283 /**
284 * Does nothing. Just to satisfy ComponentListener.
285 */
286 public void componentMoved(ComponentEvent e) {}
287 /**
288 * Does nothing. Just to satisfy ComponentListener.
289 */
290 public void componentShown(ComponentEvent e) {}
291 /**
292 * Does nothing. Just to satisfy ComponentListener.
293 */
294 public void componentHidden(ComponentEvent e) {}
295
296 /**
297 * Notify from the projection, that something has changed.
298 * @param e
299 */
300 public void stateChanged(ChangeEvent e) {
301 projection.init(dataSet);
302 recalculateCenterScale();
303 }
304
305 /**
306 * Set a new projection method. This call is not cheap, as it will
307 * transform the whole underlying dataSet and repaint itself.
308 *
309 * @param projection The new projection method to set.
310 */
311 public void setProjection(Projection projection) {
312 if (projection == this.projection)
313 return;
314 if (this.projection != null)
315 this.projection.removeChangeListener(this);
316 this.projection = projection;
317 projection.addChangeListener(this);
318 stateChanged(new ChangeEvent(this));
319 }
320
321 /**
322 * Return the projection method for this map view.
323 * @return The projection method.
324 */
325 public Projection getProjection() {
326 return projection;
327 }
328
329 /**
330 * @return Returns the autoScale.
331 */
332 public boolean isAutoScale() {
333 return autoScale;
334 }
335
336 /**
337 * @param autoScale The autoScale to set.
338 */
339 public void setAutoScale(boolean autoScale) {
340 if (this.autoScale != autoScale) {
341 this.autoScale = autoScale;
342 fireStateChanged();
343 }
344 }
345 /**
346 * @return Returns the center point. A copy is returned, so users cannot
347 * change the center by accessing the return value. Use zoomTo instead.
348 */
349 public GeoPoint getCenter() {
350 return center.clone();
351 }
352}
Note: See TracBrowser for help on using the repository browser.