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

Last change on this file since 100 was 100, checked in by imi, 18 years ago
  • fixed JOSM crash when importing GeoImages on layers without timestamp
  • fixed merging: incomplete segments do not overwrite complete on ways
  • fixed focus when entering the popups from PropertyDialog
  • fixed broken "draw lines between gps points"
  • added doubleclick on bookmarklist
  • added background color configuration
  • added GpxImport to import 1.0 and 1.1 GPX files

This is release JOSM 1.3

File size: 10.5 KB
Line 
1package org.openstreetmap.josm.gui;
2
3import java.awt.Color;
4import java.awt.Graphics;
5import java.awt.Point;
6import java.awt.event.ComponentAdapter;
7import java.awt.event.ComponentEvent;
8import java.awt.event.MouseAdapter;
9import java.awt.event.MouseEvent;
10import java.beans.PropertyChangeEvent;
11import java.beans.PropertyChangeListener;
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.LinkedList;
16
17import javax.swing.JOptionPane;
18import javax.swing.JSlider;
19import javax.swing.event.ChangeEvent;
20import javax.swing.event.ChangeListener;
21
22import org.openstreetmap.josm.Main;
23import org.openstreetmap.josm.actions.AutoScaleAction;
24import org.openstreetmap.josm.data.Bounds;
25import org.openstreetmap.josm.data.SelectionChangedListener;
26import org.openstreetmap.josm.data.coor.EastNorth;
27import org.openstreetmap.josm.data.coor.LatLon;
28import org.openstreetmap.josm.data.osm.OsmPrimitive;
29import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
30import org.openstreetmap.josm.data.osm.visitor.SimplePaintVisitor;
31import org.openstreetmap.josm.data.projection.Projection;
32import org.openstreetmap.josm.gui.layer.Layer;
33import org.openstreetmap.josm.gui.layer.OsmDataLayer;
34import org.openstreetmap.josm.gui.layer.WmsServerLayer;
35import org.openstreetmap.josm.gui.layer.OsmDataLayer.ModifiedChangedListener;
36
37/**
38 * This is a component used in the MapFrame for browsing the map. It use is to
39 * provide the MapMode's enough capabilities to operate.
40 *
41 * MapView hold meta-data about the data set currently displayed, as scale level,
42 * center point viewed, what scrolling mode or editing mode is selected or with
43 * what projection the map is viewed etc..
44 *
45 * MapView is able to administrate several layers, but there must be always at
46 * least one layer with a dataset in it (Layer.getDataSet returning non-null).
47 *
48 * @author imi
49 */
50public class MapView extends NavigatableComponent {
51
52 /**
53 * Interface to notify listeners of the change of the active layer.
54 * @author imi
55 */
56 public interface LayerChangeListener {
57 void activeLayerChange(Layer oldLayer, Layer newLayer);
58 void layerAdded(Layer newLayer);
59 void layerRemoved(Layer oldLayer);
60 }
61
62 /**
63 * Whether to adjust the scale property on every resize.
64 */
65 private boolean autoScale = true;
66
67 /**
68 * A list of all layers currently loaded.
69 */
70 private ArrayList<Layer> layers = new ArrayList<Layer>();
71 /**
72 * Direct link to the edit layer (if any) in the layers list.
73 */
74 public OsmDataLayer editLayer;
75 /**
76 * The layer from the layers list that is currently active.
77 */
78 private Layer activeLayer;
79 /**
80 * The listener of the active layer changes.
81 */
82 private Collection<LayerChangeListener> listeners = new LinkedList<LayerChangeListener>();
83
84 private final AutoScaleAction autoScaleAction;
85
86
87 private final class Scaler extends JSlider implements PropertyChangeListener, ChangeListener {
88 boolean clicked = false;
89 public Scaler() {
90 super(0, 20);
91 addMouseListener(new MouseAdapter(){
92 @Override public void mousePressed(MouseEvent e) {
93 clicked = true;
94 }
95 @Override public void mouseReleased(MouseEvent e) {
96 clicked = false;
97 }
98 });
99 MapView.this.addPropertyChangeListener(this);
100 addChangeListener(this);
101 }
102 public void propertyChange(PropertyChangeEvent evt) {
103 if (evt.getPropertyName().equals("scale") && !getModel().getValueIsAdjusting())
104 setValue(zoom());
105 }
106 public void stateChanged(ChangeEvent e) {
107 if (!clicked)
108 return;
109 EastNorth pos = world;
110 for (int zoom = 0; zoom < getValue(); ++zoom)
111 pos = new EastNorth(pos.east()/2, pos.north()/2);
112 if (MapView.this.getWidth() < MapView.this.getHeight())
113 zoomTo(center, pos.east()*2/(MapView.this.getWidth()-20));
114 else
115 zoomTo(center, pos.north()*2/(MapView.this.getHeight()-20));
116 }
117 }
118
119 public MapView(AutoScaleAction autoScaleAction) {
120 this.autoScaleAction = autoScaleAction;
121 addComponentListener(new ComponentAdapter(){
122 @Override public void componentResized(ComponentEvent e) {
123 recalculateCenterScale();
124 }
125 });
126 new MapMover(this);
127
128 // listend to selection changes to redraw the map
129 Main.ds.addSelectionChangedListener(new SelectionChangedListener(){
130 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
131 repaint();
132 }
133 });
134 Scaler zoomScaler = new Scaler();
135 zoomScaler.setOpaque(false);
136 add(zoomScaler);
137 zoomScaler.setBounds(0,0, 100, 30);
138 }
139
140 /**
141 * Add a layer to the current MapView. The layer will be added at topmost
142 * position.
143 */
144 public void addLayer(Layer layer) {
145 if (layer instanceof OsmDataLayer) {
146 final OsmDataLayer dataLayer = (OsmDataLayer)layer;
147 if (editLayer != null) {
148 editLayer.mergeFrom(layer);
149 repaint();
150 return;
151 }
152 editLayer = dataLayer;
153 dataLayer.data.addAllSelectionListener(Main.ds);
154 Main.ds = dataLayer.data;
155 dataLayer.listenerModified.add(new ModifiedChangedListener(){
156 public void modifiedChanged(boolean value, OsmDataLayer source) {
157 JOptionPane.getFrameForComponent(Main.parent).setTitle((value?"*":"")+"Java Open Street Map - Editor");
158 }
159 });
160 }
161
162 // add as a new layer
163 if (layer instanceof WmsServerLayer)
164 layers.add(layers.size(), layer);
165 else
166 layers.add(0, layer);
167
168 for (LayerChangeListener l : listeners)
169 l.layerAdded(layer);
170
171 // autoselect the new layer
172 setActiveLayer(layer);
173 }
174
175 /**
176 * Remove the layer from the mapview. If the layer was in the list before,
177 * an LayerChange event is fired.
178 */
179 public void removeLayer(Layer layer) {
180 if (layers.remove(layer))
181 for (LayerChangeListener l : listeners)
182 l.layerRemoved(layer);
183 if (layer == editLayer)
184 editLayer = null;
185 }
186
187 /**
188 * Moves the layer to the given new position. No event is fired.
189 * @param layer The layer to move
190 * @param pos The new position of the layer
191 */
192 public void moveLayer(Layer layer, int pos) {
193 int curLayerPos = layers.indexOf(layer);
194 if (curLayerPos == -1)
195 throw new IllegalArgumentException("layer not in list.");
196 if (pos == curLayerPos)
197 return; // already in place.
198 layers.remove(curLayerPos);
199 if (pos >= layers.size())
200 layers.add(layer);
201 else
202 layers.add(pos, layer);
203 }
204
205 /**
206 * Draw the component.
207 */
208 @Override public void paint(Graphics g) {
209 g.setColor(SimplePaintVisitor.getPreferencesColor("background", Color.BLACK));
210 g.fillRect(0, 0, getWidth(), getHeight());
211
212 for (int i = layers.size()-1; i >= 0; --i) {
213 Layer l = layers.get(i);
214 if (l.visible)
215 l.paint(g, this);
216 }
217
218 // draw world borders
219 g.setColor(Color.WHITE);
220 Bounds b = new Bounds();
221 Point min = getPoint(getProjection().latlon2eastNorth(b.min));
222 Point max = getPoint(getProjection().latlon2eastNorth(b.max));
223 int x1 = Math.min(min.x, max.x);
224 int y1 = Math.min(min.y, max.y);
225 int x2 = Math.max(min.x, max.x);
226 int y2 = Math.max(min.y, max.y);
227 if (x1 > 0 || y1 > 0 || x2 < getWidth() || y2 < getHeight())
228 g.drawRect(x1, y1, x2-x1+1, y2-y1+1);
229 super.paint(g);
230 }
231
232 /**
233 * @return Returns the autoScale.
234 */
235 public boolean isAutoScale() {
236 return autoScale;
237 }
238
239 /**
240 * @param autoScale The autoScale to set.
241 */
242 public void setAutoScale(boolean autoScale) {
243 if (this.autoScale != autoScale) {
244 this.autoScale = autoScale;
245 firePropertyChange("autoScale", !autoScale, autoScale);
246 recalculateCenterScale();
247 }
248 }
249 /**
250 * Set the new dimension to the projection class. Also adjust the components
251 * scale, if in autoScale mode.
252 */
253 public void recalculateCenterScale() {
254 if (autoScale) {
255 // -20 to leave some border
256 int w = getWidth()-20;
257 if (w < 20)
258 w = 20;
259 int h = getHeight()-20;
260 if (h < 20)
261 h = 20;
262
263 BoundingXYVisitor v = autoScaleAction.getBoundingBox();
264
265 boolean oldAutoScale = autoScale;
266 EastNorth oldCenter = center;
267 double oldScale = this.scale;
268
269 if (v.min == null || v.max == null || v.min.equals(v.max)) {
270 // no bounds means whole world
271 center = getProjection().latlon2eastNorth(new LatLon(0,0));
272 EastNorth world = getProjection().latlon2eastNorth(new LatLon(Projection.MAX_LAT,Projection.MAX_LON));
273 double scaleX = world.east()*2/w;
274 double scaleY = world.north()*2/h;
275 scale = Math.max(scaleX, scaleY); // minimum scale to see all of the screen
276 } else {
277 center = new EastNorth(v.min.east()/2+v.max.east()/2, v.min.north()/2+v.max.north()/2);
278 double scaleX = (v.max.east()-v.min.east())/w;
279 double scaleY = (v.max.north()-v.min.north())/h;
280 scale = Math.max(scaleX, scaleY); // minimum scale to see all of the screen
281 }
282
283 if (!center.equals(oldCenter))
284 firePropertyChange("center", oldCenter, center);
285 if (oldAutoScale != autoScale)
286 firePropertyChange("autoScale", oldAutoScale, autoScale);
287 if (oldScale != scale)
288 firePropertyChange("scale", oldScale, scale);
289 }
290 repaint();
291 }
292
293 /**
294 * Add a listener for changes of active layer.
295 * @param listener The listener that get added.
296 */
297 public void addLayerChangeListener(LayerChangeListener listener) {
298 if (listener != null)
299 listeners.add(listener);
300 }
301
302 /**
303 * Remove the listener.
304 * @param listener The listener that get removed from the list.
305 */
306 public void removeLayerChangeListener(LayerChangeListener listener) {
307 listeners.remove(listener);
308 }
309
310 /**
311 * @return An unmodificable list of all layers
312 */
313 public Collection<Layer> getAllLayers() {
314 return Collections.unmodifiableCollection(layers);
315 }
316
317 /**
318 * Set the active selection to the given value and raise an layerchange event.
319 */
320 public void setActiveLayer(Layer layer) {
321 if (!layers.contains(layer))
322 throw new IllegalArgumentException("layer must be in layerlist");
323 Layer old = activeLayer;
324 activeLayer = layer;
325 if (old != layer) {
326 for (LayerChangeListener l : listeners)
327 l.activeLayerChange(old, layer);
328 recalculateCenterScale();
329 }
330 }
331
332 /**
333 * @return The current active layer
334 */
335 public Layer getActiveLayer() {
336 return activeLayer;
337 }
338
339 /**
340 * In addition to the base class funcitonality, this keep trak of the autoscale
341 * feature.
342 */
343 @Override public void zoomTo(EastNorth newCenter, double scale) {
344 boolean oldAutoScale = autoScale;
345 EastNorth oldCenter = center;
346 double oldScale = this.scale;
347 autoScale = false;
348
349 super.zoomTo(newCenter, scale);
350
351 recalculateCenterScale();
352
353 if (!oldCenter.equals(center))
354 firePropertyChange("center", oldCenter, center);
355 if (oldAutoScale != autoScale)
356 firePropertyChange("autoScale", oldAutoScale, autoScale);
357 if (oldScale != scale)
358 firePropertyChange("scale", oldScale, scale);
359 }
360}
Note: See TracBrowser for help on using the repository browser.