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

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