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

Last change on this file since 93 was 93, checked in by imi, 18 years ago
  • added "insert node into line segment" mapmode
  • added direction hint to line segments
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 org.openstreetmap.josm.Main;
14import org.openstreetmap.josm.actions.AutoScaleAction;
15import org.openstreetmap.josm.data.Bounds;
16import org.openstreetmap.josm.data.SelectionChangedListener;
17import org.openstreetmap.josm.data.coor.EastNorth;
18import org.openstreetmap.josm.data.coor.LatLon;
19import org.openstreetmap.josm.data.osm.OsmPrimitive;
20import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
21import org.openstreetmap.josm.data.projection.Projection;
22import org.openstreetmap.josm.gui.layer.Layer;
23import org.openstreetmap.josm.gui.layer.OsmDataLayer;
24import org.openstreetmap.josm.gui.layer.WmsServerLayer;
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 {
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 private 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 private final AutoScaleAction autoScaleAction;
75
76 public MapView(AutoScaleAction autoScaleAction) {
77 this.autoScaleAction = autoScaleAction;
78 addComponentListener(new ComponentAdapter(){
79 @Override public void componentResized(ComponentEvent e) {
80 recalculateCenterScale();
81 }
82 });
83 new MapMover(this);
84
85 // listend to selection changes to redraw the map
86 Main.ds.addSelectionChangedListener(new SelectionChangedListener(){
87 public void selectionChanged(Collection<OsmPrimitive> newSelection) {
88 repaint();
89 }
90 });
91 }
92
93 /**
94 * Add a layer to the current MapView. The layer will be added at topmost
95 * position.
96 */
97 public void addLayer(Layer layer) {
98 if (layer instanceof OsmDataLayer) {
99 final OsmDataLayer dataLayer = (OsmDataLayer)layer;
100 if (editLayer != null) {
101 editLayer.mergeFrom(layer);
102 repaint();
103 return;
104 }
105 editLayer = dataLayer;
106 dataLayer.data.addAllSelectionListener(Main.ds);
107 Main.ds = dataLayer.data;
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 if (layer instanceof WmsServerLayer)
117 layers.add(layers.size(), layer);
118 else
119 layers.add(0, layer);
120
121 for (LayerChangeListener l : listeners)
122 l.layerAdded(layer);
123
124 // autoselect the new layer
125 setActiveLayer(layer);
126 }
127
128 /**
129 * Remove the layer from the mapview. If the layer was in the list before,
130 * an LayerChange event is fired.
131 */
132 public void removeLayer(Layer layer) {
133 if (layers.remove(layer))
134 for (LayerChangeListener l : listeners)
135 l.layerRemoved(layer);
136 if (layer == editLayer)
137 editLayer = null;
138 }
139
140 /**
141 * Moves the layer to the given new position. No event is fired.
142 * @param layer The layer to move
143 * @param pos The new position of the layer
144 */
145 public void moveLayer(Layer layer, int pos) {
146 int curLayerPos = layers.indexOf(layer);
147 if (curLayerPos == -1)
148 throw new IllegalArgumentException("layer not in list.");
149 if (pos == curLayerPos)
150 return; // already in place.
151 layers.remove(curLayerPos);
152 if (pos >= layers.size())
153 layers.add(layer);
154 else
155 layers.add(pos, layer);
156 }
157
158 /**
159 * Draw the component.
160 */
161 @Override public void paint(Graphics g) {
162 g.setColor(Color.BLACK);
163 g.fillRect(0, 0, getWidth(), getHeight());
164
165 for (int i = layers.size()-1; i >= 0; --i) {
166 Layer l = layers.get(i);
167 if (l.visible)
168 l.paint(g, this);
169 }
170
171 // draw world borders
172 g.setColor(Color.WHITE);
173 Bounds b = new Bounds();
174 Point min = getPoint(getProjection().latlon2eastNorth(b.min));
175 Point max = getPoint(getProjection().latlon2eastNorth(b.max));
176 int x1 = Math.min(min.x, max.x);
177 int y1 = Math.min(min.y, max.y);
178 int x2 = Math.max(min.x, max.x);
179 int y2 = Math.max(min.y, max.y);
180 if (x1 > 0 || y1 > 0 || x2 < getWidth() || y2 < getHeight())
181 g.drawRect(x1, y1, x2-x1+1, y2-y1+1);
182 }
183
184 /**
185 * @return Returns the autoScale.
186 */
187 public boolean isAutoScale() {
188 return autoScale;
189 }
190
191 /**
192 * @param autoScale The autoScale to set.
193 */
194 public void setAutoScale(boolean autoScale) {
195 if (this.autoScale != autoScale) {
196 this.autoScale = autoScale;
197 firePropertyChange("autoScale", !autoScale, autoScale);
198 recalculateCenterScale();
199 }
200 }
201 /**
202 * Set the new dimension to the projection class. Also adjust the components
203 * scale, if in autoScale mode.
204 */
205 public void recalculateCenterScale() {
206 if (autoScale) {
207 // -20 to leave some border
208 int w = getWidth()-20;
209 if (w < 20)
210 w = 20;
211 int h = getHeight()-20;
212 if (h < 20)
213 h = 20;
214
215 BoundingXYVisitor v = autoScaleAction.getBoundingBox();
216
217 boolean oldAutoScale = autoScale;
218 EastNorth oldCenter = center;
219 double oldScale = this.scale;
220
221 if (v.min == null || v.max == null || v.min.equals(v.max)) {
222 // no bounds means whole world
223 center = getProjection().latlon2eastNorth(new LatLon(0,0));
224 EastNorth world = getProjection().latlon2eastNorth(new LatLon(Projection.MAX_LAT,Projection.MAX_LON));
225 double scaleX = world.east()*2/w;
226 double scaleY = world.north()*2/h;
227 scale = Math.max(scaleX, scaleY); // minimum scale to see all of the screen
228 } else {
229 center = new EastNorth(v.min.east()/2+v.max.east()/2, v.min.north()/2+v.max.north()/2);
230 double scaleX = (v.max.east()-v.min.east())/w;
231 double scaleY = (v.max.north()-v.min.north())/h;
232 scale = Math.max(scaleX, scaleY); // minimum scale to see all of the screen
233 }
234
235 if (!center.equals(oldCenter))
236 firePropertyChange("center", oldCenter, center);
237 if (oldAutoScale != autoScale)
238 firePropertyChange("autoScale", oldAutoScale, autoScale);
239 if (oldScale != scale)
240 firePropertyChange("scale", oldScale, scale);
241 }
242 repaint();
243 }
244
245 /**
246 * Add a listener for changes of active layer.
247 * @param listener The listener that get added.
248 */
249 public void addLayerChangeListener(LayerChangeListener listener) {
250 if (listener != null)
251 listeners.add(listener);
252 }
253
254 /**
255 * Remove the listener.
256 * @param listener The listener that get removed from the list.
257 */
258 public void removeLayerChangeListener(LayerChangeListener listener) {
259 listeners.remove(listener);
260 }
261
262 /**
263 * @return An unmodificable list of all layers
264 */
265 public Collection<Layer> getAllLayers() {
266 return Collections.unmodifiableCollection(layers);
267 }
268
269 /**
270 * Set the active selection to the given value and raise an layerchange event.
271 */
272 public void setActiveLayer(Layer layer) {
273 if (!layers.contains(layer))
274 throw new IllegalArgumentException("layer must be in layerlist");
275 Layer old = activeLayer;
276 activeLayer = layer;
277 if (old != layer) {
278 for (LayerChangeListener l : listeners)
279 l.activeLayerChange(old, layer);
280 recalculateCenterScale();
281 }
282 }
283
284 /**
285 * @return The current active layer
286 */
287 public Layer getActiveLayer() {
288 return activeLayer;
289 }
290
291 /**
292 * @return The current edit layer. If no edit layer exist, one is created.
293 * So editLayer does never return <code>null</code>.
294 */
295 public OsmDataLayer editLayer() {
296 if (editLayer == null)
297 addLayer(new OsmDataLayer(Main.ds, "unnamed", false));
298 return editLayer;
299 }
300
301 /**
302 * In addition to the base class funcitonality, this keep trak of the autoscale
303 * feature.
304 */
305 @Override public void zoomTo(EastNorth newCenter, double scale) {
306 boolean oldAutoScale = autoScale;
307 EastNorth oldCenter = center;
308 double oldScale = this.scale;
309 autoScale = false;
310
311 super.zoomTo(newCenter, scale);
312
313 recalculateCenterScale();
314
315 if (!oldCenter.equals(center))
316 firePropertyChange("center", oldCenter, center);
317 if (oldAutoScale != autoScale)
318 firePropertyChange("autoScale", oldAutoScale, autoScale);
319 if (oldScale != scale)
320 firePropertyChange("scale", oldScale, scale);
321 }
322}
Note: See TracBrowser for help on using the repository browser.