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

Last change on this file since 74 was 74, checked in by imi, 18 years ago

changed Preferences system to more flexible one.

File size: 9.1 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.util.ArrayList;
9import java.util.Collection;
10import java.util.Collections;
11import java.util.LinkedList;
12
13import javax.swing.event.ChangeEvent;
14import javax.swing.event.ChangeListener;
15
16import org.openstreetmap.josm.Main;
17import org.openstreetmap.josm.data.Bounds;
18import org.openstreetmap.josm.data.coor.EastNorth;
19import org.openstreetmap.josm.data.coor.LatLon;
20import org.openstreetmap.josm.data.osm.DataSet;
21import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
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 {
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 new MapMover(this);
86 addLayer(layer);
87 }
88
89 /**
90 * Add a layer to the current MapView. The layer will be added at topmost
91 * position.
92 */
93 public void addLayer(Layer layer) {
94 // reinitialize layer's data
95 layer.init(getProjection());
96
97 if (layer instanceof OsmDataLayer) {
98 final OsmDataLayer dataLayer = (OsmDataLayer)layer;
99 if (editLayer != null) {
100 // merge the layer into the existing one
101 if (!editLayer.isMergable(layer))
102 throw new IllegalArgumentException("Cannot merge argument");
103 editLayer.mergeFrom(layer);
104 repaint();
105 return;
106 }
107 editLayer = dataLayer;
108 dataLayer.addModifiedListener(new ModifiedChangedListener(){
109 public void modifiedChanged(boolean value, OsmDataLayer source) {
110 Main.main.setTitle((value?"*":"")+"Java Open Street Map - Editor");
111 }
112 });
113 }
114
115 // add as a new layer
116 layers.add(0,layer);
117
118 for (LayerChangeListener l : listeners)
119 l.layerAdded(layer);
120
121 // autoselect the new layer
122 setActiveLayer(layer);
123 recalculateCenterScale();
124 }
125
126 /**
127 * Remove the layer from the mapview. If the layer was in the list before,
128 * an LayerChange event is fired.
129 */
130 public void removeLayer(Layer layer) {
131 if (layers.remove(layer))
132 for (LayerChangeListener l : listeners)
133 l.layerRemoved(layer);
134 if (layer == editLayer)
135 editLayer = null;
136 }
137
138 /**
139 * Moves the layer to the given new position. No event is fired.
140 * @param layer The layer to move
141 * @param pos The new position of the layer
142 */
143 public void moveLayer(Layer layer, int pos) {
144 int curLayerPos = layers.indexOf(layer);
145 if (curLayerPos == -1)
146 throw new IllegalArgumentException("layer not in list.");
147 if (pos == curLayerPos)
148 return; // already in place.
149 layers.remove(curLayerPos);
150 if (pos >= layers.size())
151 layers.add(layer);
152 else
153 layers.add(pos, layer);
154 }
155
156 /**
157 * Draw the component.
158 */
159 @Override
160 public void paint(Graphics g) {
161 g.setColor(Color.BLACK);
162 g.fillRect(0, 0, getWidth(), getHeight());
163
164 for (int i = layers.size()-1; i >= 0; --i) {
165 Layer l = layers.get(i);
166 if (l.visible)
167 l.paint(g, this);
168 }
169
170 // draw world borders
171 g.setColor(Color.WHITE);
172 Bounds b = new Bounds();
173 Point min = getPoint(getProjection().latlon2eastNorth(b.min));
174 Point max = getPoint(getProjection().latlon2eastNorth(b.max));
175 int x1 = Math.min(min.x, max.x);
176 int y1 = Math.min(min.y, max.y);
177 int x2 = Math.max(min.x, max.x);
178 int y2 = Math.max(min.y, max.y);
179 if (x1 > 0 || y1 > 0 || x2 < getWidth() || y2 < getHeight())
180 g.drawRect(x1, y1, x2-x1+1, y2-y1+1);
181 }
182
183 /**
184 * @return Returns the autoScale.
185 */
186 public boolean isAutoScale() {
187 return autoScale;
188 }
189
190 /**
191 * @param autoScale The autoScale to set.
192 */
193 public void setAutoScale(boolean autoScale) {
194 if (this.autoScale != autoScale) {
195 this.autoScale = autoScale;
196 firePropertyChange("autoScale", !autoScale, autoScale);
197 recalculateCenterScale();
198 }
199 }
200 /**
201 * Set the new dimension to the projection class. Also adjust the components
202 * scale, if in autoScale mode.
203 */
204 void recalculateCenterScale() {
205 if (autoScale) {
206 // -20 to leave some border
207 int w = getWidth()-20;
208 if (w < 20)
209 w = 20;
210 int h = getHeight()-20;
211 if (h < 20)
212 h = 20;
213
214 BoundingXYVisitor v = new BoundingXYVisitor();
215 for (Layer l : layers)
216 l.visitBoundingBox(v);
217
218 boolean oldAutoScale = autoScale;
219 EastNorth oldCenter = center;
220 double oldScale = this.scale;
221
222 if (v.min == null || v.max == null) {
223 // no bounds means standard scale and center
224 center = Main.proj.latlon2eastNorth(new LatLon(51.526447, -0.14746371));
225 scale = 10;
226 } else {
227 center = new EastNorth(v.min.east()/2+v.max.east()/2, v.min.north()/2+v.max.north()/2);
228 double scaleX = (v.max.east()-v.min.east())/w;
229 double scaleY = (v.max.north()-v.min.north())/h;
230 scale = Math.max(scaleX, scaleY); // minimum scale to see all of the screen
231 }
232
233 if (!center.equals(oldCenter))
234 firePropertyChange("center", oldCenter, center);
235 if (oldAutoScale != autoScale)
236 firePropertyChange("autoScale", oldAutoScale, autoScale);
237 if (oldScale != scale)
238 firePropertyChange("scale", oldScale, scale);
239 }
240 repaint();
241 }
242
243 /**
244 * Add a listener for changes of active layer.
245 * @param listener The listener that get added.
246 */
247 public void addLayerChangeListener(LayerChangeListener listener) {
248 if (listener != null)
249 listeners.add(listener);
250 }
251
252 /**
253 * Remove the listener.
254 * @param listener The listener that get removed from the list.
255 */
256 public void removeLayerChangeListener(LayerChangeListener listener) {
257 listeners.remove(listener);
258 }
259
260 /**
261 * @return An unmodificable list of all layers
262 */
263 public Collection<Layer> getAllLayers() {
264 return Collections.unmodifiableCollection(layers);
265 }
266
267 /**
268 * Set the active selection to the given value and raise an layerchange event.
269 * Also, swap the active dataset in Main.main if it is a datalayer.
270 */
271 public void setActiveLayer(Layer layer) {
272 if (!layers.contains(layer))
273 throw new IllegalArgumentException("layer must be in layerlist");
274 Layer old = activeLayer;
275 activeLayer = layer;
276 if (layer instanceof OsmDataLayer)
277 Main.main.ds = ((OsmDataLayer)layer).data;
278 if (old != layer) {
279 for (LayerChangeListener l : listeners)
280 l.activeLayerChange(old, layer);
281 recalculateCenterScale();
282 }
283 }
284
285 /**
286 * @return The current active layer
287 */
288 public Layer getActiveLayer() {
289 return activeLayer;
290 }
291
292 /**
293 * @return The current edit layer. If no edit layer exist, one is created.
294 * So editLayer does never return <code>null</code>.
295 */
296 public OsmDataLayer editLayer() {
297 if (editLayer == null)
298 addLayer(new OsmDataLayer(new DataSet(), "unnamed", false));
299 return editLayer;
300 }
301
302 /**
303 * In addition to the base class funcitonality, this keep trak of the autoscale
304 * feature.
305 */
306 @Override
307 public void zoomTo(EastNorth newCenter, double scale) {
308 boolean oldAutoScale = autoScale;
309 EastNorth oldCenter = center;
310 double oldScale = this.scale;
311 autoScale = false;
312
313 super.zoomTo(newCenter, scale);
314
315 recalculateCenterScale();
316
317 if (!oldCenter.equals(center))
318 firePropertyChange("center", oldCenter, center);
319 if (oldAutoScale != autoScale)
320 firePropertyChange("autoScale", oldAutoScale, autoScale);
321 if (oldScale != scale)
322 firePropertyChange("scale", oldScale, scale);
323 }
324
325 /**
326 * Notify from the projection, that something has changed.
327 */
328 public void stateChanged(ChangeEvent e) {
329 // reset all datasets.
330 Projection p = getProjection();
331 for (Layer l : layers)
332 l.init(p);
333 recalculateCenterScale();
334 }
335}
Note: See TracBrowser for help on using the repository browser.