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

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