source: josm/trunk/src/org/openstreetmap/josm/actions/AutoScaleAction.java@ 8171

Last change on this file since 8171 was 8171, checked in by stoecker, 10 years ago

fix #7630 - provide a zoom to all downloaded areas function

  • Property svn:eol-style set to native
File size: 14.9 KB
RevLine 
[6380]1// License: GPL. For details, see LICENSE file.
[403]2package org.openstreetmap.josm.actions;
3
[2477]4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
[948]5import static org.openstreetmap.josm.tools.I18n.marktr;
[403]6import static org.openstreetmap.josm.tools.I18n.tr;
7
8import java.awt.event.ActionEvent;
[458]9import java.awt.event.KeyEvent;
[8171]10import java.util.ArrayList;
[6246]11import java.util.Arrays;
[403]12import java.util.Collection;
[6246]13import java.util.Collections;
[1750]14import java.util.HashSet;
[1953]15import java.util.List;
[403]16
17import javax.swing.JOptionPane;
[5958]18import javax.swing.event.ListSelectionEvent;
19import javax.swing.event.ListSelectionListener;
20import javax.swing.event.TreeSelectionEvent;
21import javax.swing.event.TreeSelectionListener;
[403]22
23import org.openstreetmap.josm.Main;
[2477]24import org.openstreetmap.josm.data.Bounds;
[8171]25import org.openstreetmap.josm.data.DataSource;
[3973]26import org.openstreetmap.josm.data.conflict.Conflict;
[403]27import org.openstreetmap.josm.data.osm.OsmPrimitive;
28import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
[5958]29import org.openstreetmap.josm.data.validation.TestError;
30import org.openstreetmap.josm.gui.MapFrame;
31import org.openstreetmap.josm.gui.MapFrameListener;
[2759]32import org.openstreetmap.josm.gui.MapView;
[1953]33import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
[5958]34import org.openstreetmap.josm.gui.dialogs.ValidatorDialog.ValidatorBoundingXYVisitor;
[403]35import org.openstreetmap.josm.gui.layer.Layer;
[1084]36import org.openstreetmap.josm.tools.Shortcut;
[403]37
38/**
39 * Toggles the autoScale feature of the mapView
40 * @author imi
41 */
42public class AutoScaleAction extends JosmAction {
43
[6246]44 public static final Collection<String> MODES = Collections.unmodifiableList(Arrays.asList(
[7668]45 marktr(/* ICON(dialogs/autoscale/) */ "data"),
46 marktr(/* ICON(dialogs/autoscale/) */ "layer"),
47 marktr(/* ICON(dialogs/autoscale/) */ "selection"),
48 marktr(/* ICON(dialogs/autoscale/) */ "conflict"),
49 marktr(/* ICON(dialogs/autoscale/) */ "download"),
50 marktr(/* ICON(dialogs/autoscale/) */ "problem"),
51 marktr(/* ICON(dialogs/autoscale/) */ "previous"),
52 marktr(/* ICON(dialogs/autoscale/) */ "next")));
[6069]53
[5958]54 private final String mode;
[2685]55
[5958]56 protected ZoomChangeAdapter zoomChangeAdapter;
57 protected MapFrameAdapter mapFrameAdapter;
[8171]58 /** Time of last zoom to bounds action */
59 protected long lastZoomTime = -1;
60 /** Last zommed bounds */
61 protected int lastZoomArea = -1;
[5958]62
[2685]63 /**
64 * Zooms the current map view to the currently selected primitives.
65 * Does nothing if there either isn't a current map view or if there isn't a current data
66 * layer.
[2711]67 *
[2685]68 */
69 public static void zoomToSelection() {
[8171]70 if (Main.main == null || !Main.main.hasEditLayer())
71 return;
[2986]72 Collection<OsmPrimitive> sel = Main.main.getEditLayer().data.getSelected();
[2685]73 if (sel.isEmpty()) {
74 JOptionPane.showMessageDialog(
75 Main.parent,
76 tr("Nothing selected to zoom to."),
77 tr("Information"),
[8171]78 JOptionPane.INFORMATION_MESSAGE);
[2685]79 return;
80 }
[3251]81 zoomTo(sel);
82 }
83
84 public static void zoomTo(Collection<OsmPrimitive> sel) {
[2685]85 BoundingXYVisitor bboxCalculator = new BoundingXYVisitor();
86 bboxCalculator.computeBoundingBox(sel);
87 // increase bbox by 0.001 degrees on each side. this is required
88 // especially if the bbox contains one single node, but helpful
89 // in most other cases as well.
90 bboxCalculator.enlargeBoundingBox();
91 if (bboxCalculator.getBounds() != null) {
[7817]92 Main.map.mapView.zoomTo(bboxCalculator);
[2685]93 }
94 }
95
[3327]96 public static void autoScale(String mode) {
97 new AutoScaleAction(mode, false).autoScale();
98 }
99
[948]100 private static int getModeShortcut(String mode) {
101 int shortcut = -1;
[458]102
[7012]103 // TODO: convert this to switch/case and make sure the parsing still works
[4921]104 /* leave as single line for shortcut overview parsing! */
105 if (mode.equals("data")) { shortcut = KeyEvent.VK_1; }
106 else if (mode.equals("layer")) { shortcut = KeyEvent.VK_2; }
107 else if (mode.equals("selection")) { shortcut = KeyEvent.VK_3; }
108 else if (mode.equals("conflict")) { shortcut = KeyEvent.VK_4; }
109 else if (mode.equals("download")) { shortcut = KeyEvent.VK_5; }
[5958]110 else if (mode.equals("problem")) { shortcut = KeyEvent.VK_6; }
[4921]111 else if (mode.equals("previous")) { shortcut = KeyEvent.VK_8; }
112 else if (mode.equals("next")) { shortcut = KeyEvent.VK_9; }
[458]113
[948]114 return shortcut;
115 }
[403]116
[3327]117 /**
[5958]118 * Constructs a new {@code AutoScaleAction}.
119 * @param mode The autoscale mode (one of {@link AutoScaleAction#MODES})
[3327]120 * @param marker Used only to differentiate from default constructor
121 */
122 private AutoScaleAction(String mode, boolean marker) {
123 super(false);
124 this.mode = mode;
125 }
126
[5958]127 /**
128 * Constructs a new {@code AutoScaleAction}.
129 * @param mode The autoscale mode (one of {@link AutoScaleAction#MODES})
130 */
131 public AutoScaleAction(final String mode) {
[948]132 super(tr("Zoom to {0}", tr(mode)), "dialogs/autoscale/" + mode, tr("Zoom the view to {0}.", tr(mode)),
[8171]133 Shortcut.registerShortcut("view:zoom" + mode, tr("View: {0}", tr("Zoom to {0}", tr(mode))),
134 getModeShortcut(mode), Shortcut.DIRECT), true, null, false);
[948]135 String modeHelp = Character.toUpperCase(mode.charAt(0)) + mode.substring(1);
136 putValue("help", "Action/AutoScale/" + modeHelp);
137 this.mode = mode;
[7012]138 switch (mode) {
139 case "data":
[2323]140 putValue("help", ht("/Action/ZoomToData"));
[7012]141 break;
142 case "layer":
[2323]143 putValue("help", ht("/Action/ZoomToLayer"));
[7012]144 break;
145 case "selection":
[2323]146 putValue("help", ht("/Action/ZoomToSelection"));
[7012]147 break;
148 case "conflict":
[2323]149 putValue("help", ht("/Action/ZoomToConflict"));
[7012]150 break;
151 case "problem":
[5958]152 putValue("help", ht("/Action/ZoomToProblem"));
[7012]153 break;
154 case "download":
[2323]155 putValue("help", ht("/Action/ZoomToDownload"));
[7012]156 break;
157 case "previous":
[3760]158 putValue("help", ht("/Action/ZoomToPrevious"));
[7012]159 break;
160 case "next":
[3760]161 putValue("help", ht("/Action/ZoomToNext"));
[7012]162 break;
163 default:
[8171]164 throw new IllegalArgumentException("Unknown mode: " + mode);
[2477]165 }
[5958]166 installAdapters();
[948]167 }
[403]168
[8171]169 public void autoScale() {
[5460]170 if (Main.isDisplayingMapView()) {
[8171]171 switch (mode) {
[7012]172 case "previous":
[2758]173 Main.map.mapView.zoomPrevious();
[7012]174 break;
175 case "next":
[2758]176 Main.map.mapView.zoomNext();
[7012]177 break;
178 default:
[2758]179 BoundingXYVisitor bbox = getBoundingBox();
180 if (bbox != null && bbox.getBounds() != null) {
[7817]181 Main.map.mapView.zoomTo(bbox);
[2758]182 }
[948]183 }
184 }
185 putValue("active", true);
186 }
187
[6084]188 @Override
[1868]189 public void actionPerformed(ActionEvent e) {
190 autoScale();
191 }
192
[1953]193 /**
194 * Replies the first selected layer in the layer list dialog. null, if no
195 * such layer exists, either because the layer list dialog is not yet created
196 * or because no layer is selected.
[2512]197 *
[1953]198 * @return the first selected layer in the layer list dialog
199 */
200 protected Layer getFirstSelectedLayer() {
201 List<Layer> layers = LayerListDialog.getInstance().getModel().getSelectedLayers();
[8171]202 if (layers.isEmpty())
203 return null;
[1953]204 return layers.get(0);
205 }
206
[948]207 private BoundingXYVisitor getBoundingBox() {
[7012]208 BoundingXYVisitor v = "problem".equals(mode) ? new ValidatorBoundingXYVisitor() : new BoundingXYVisitor();
[5958]209
[8171]210 switch (mode) {
[7012]211 case "problem":
[5958]212 TestError error = Main.map.validatorDialog.getSelectedError();
[8171]213 if (error == null)
214 return null;
[5958]215 ((ValidatorBoundingXYVisitor) v).visit(error);
[8171]216 if (v.getBounds() == null)
217 return null;
[5958]218 v.enlargeBoundingBox(Main.pref.getDouble("validator.zoom-enlarge-bbox", 0.0002));
[7012]219 break;
220 case "data":
[1750]221 for (Layer l : Main.map.mapView.getAllLayers()) {
[948]222 l.visitBoundingBox(v);
[1750]223 }
[7012]224 break;
225 case "layer":
[6783]226 if (Main.main.getActiveLayer() == null)
[1903]227 return null;
[1953]228 // try to zoom to the first selected layer
229 Layer l = getFirstSelectedLayer();
[8171]230 if (l == null)
231 return null;
[1953]232 l.visitBoundingBox(v);
[7012]233 break;
234 case "selection":
235 case "conflict":
[7005]236 Collection<OsmPrimitive> sel = new HashSet<>();
[7012]237 if ("selection".equals(mode)) {
[1814]238 sel = getCurrentDataSet().getSelected();
[7012]239 } else {
[3973]240 Conflict<? extends OsmPrimitive> c = Main.map.conflictDialog.getSelectedConflict();
241 if (c != null) {
242 sel.add(c.getMy());
243 } else if (Main.map.conflictDialog.getConflicts() != null) {
[1750]244 sel = Main.map.conflictDialog.getConflicts().getMyConflictParties();
245 }
246 }
[948]247 if (sel.isEmpty()) {
[2017]248 JOptionPane.showMessageDialog(
[1847]249 Main.parent,
[7012]250 ("selection".equals(mode) ? tr("Nothing selected to zoom to.") : tr("No conflicts to zoom to")),
[1847]251 tr("Information"),
[8171]252 JOptionPane.INFORMATION_MESSAGE);
[948]253 return null;
254 }
[1750]255 for (OsmPrimitive osm : sel) {
[6009]256 osm.accept(v);
[1750]257 }
[6608]258
259 // Increase the bounding box by up to 100% to give more context.
260 v.enlargeBoundingBoxLogarithmically(100);
[7053]261 // Make the bounding box at least 100 meter wide to
[6608]262 // ensure reasonable zoom level when zooming onto single nodes.
[7053]263 v.enlargeToMinSize(Main.pref.getDouble("zoom_to_selection_min_size_in_meter", 100));
[7012]264 break;
265 case "download":
[8171]266
267 if (lastZoomTime > 0 && System.currentTimeMillis() - lastZoomTime > Main.pref.getLong("zoom.bounds.reset.time", 10*1000)) {
268 lastZoomTime = -1;
269 }
270 ArrayList<DataSource> dataSources = new ArrayList<>(Main.main.getCurrentDataSet().getDataSources());
271 int s = dataSources.size();
272 if(s > 0) {
273 if(lastZoomTime == -1 || lastZoomArea == -1 || lastZoomArea > s) {
274 lastZoomArea = s-1;
275 v.visit(dataSources.get(lastZoomArea).bounds);
276 } else if(lastZoomArea > 0) {
277 lastZoomArea -= 1;
278 v.visit(dataSources.get(lastZoomArea).bounds);
279 } else {
280 lastZoomArea = -1;
281 v.visit(new Bounds(Main.main.getCurrentDataSet().getDataSourceArea().getBounds2D()));
[1302]282 }
[8171]283 lastZoomTime = System.currentTimeMillis();
284 } else {
285 lastZoomTime = -1;
286 lastZoomArea = -1;
[1302]287 }
[7012]288 break;
[1302]289 }
[948]290 return v;
291 }
[1820]292
293 @Override
294 protected void updateEnabledState() {
[8171]295 switch (mode) {
[7012]296 case "selection":
[8171]297 setEnabled(getCurrentDataSet() != null && !getCurrentDataSet().getSelected().isEmpty());
[7012]298 break;
299 case "layer":
[6333]300 if (!Main.isDisplayingMapView() || Main.map.mapView.getAllLayersAsList().isEmpty()) {
[1953]301 setEnabled(false);
302 } else {
303 // FIXME: should also check for whether a layer is selected in the layer list dialog
304 setEnabled(true);
305 }
[7012]306 break;
307 case "conflict":
[5958]308 setEnabled(Main.map != null && Main.map.conflictDialog.getSelectedConflict() != null);
[7012]309 break;
310 case "problem":
[5958]311 setEnabled(Main.map != null && Main.map.validatorDialog.getSelectedError() != null);
[7012]312 break;
313 case "previous":
[5460]314 setEnabled(Main.isDisplayingMapView() && Main.map.mapView.hasZoomUndoEntries());
[7012]315 break;
316 case "next":
[5460]317 setEnabled(Main.isDisplayingMapView() && Main.map.mapView.hasZoomRedoEntries());
[7012]318 break;
319 default:
[8171]320 setEnabled(Main.isDisplayingMapView() && Main.map.mapView.hasLayers());
[1854]321 }
[1820]322 }
[2256]323
324 @Override
325 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
326 if ("selection".equals(mode)) {
327 setEnabled(selection != null && !selection.isEmpty());
328 }
329 }
[2759]330
331 @Override
[6890]332 protected final void installAdapters() {
[2759]333 super.installAdapters();
[5958]334 // make this action listen to zoom and mapframe change events
[2759]335 //
[5958]336 MapView.addZoomChangeListener(zoomChangeAdapter = new ZoomChangeAdapter());
337 Main.addMapFrameListener(mapFrameAdapter = new MapFrameAdapter());
[2759]338 initEnabledState();
339 }
340
341 /**
[5958]342 * Adapter for zoom change events
[2759]343 */
344 private class ZoomChangeAdapter implements MapView.ZoomChangeListener {
[6084]345 @Override
[2759]346 public void zoomChanged() {
347 updateEnabledState();
348 }
349 }
350
[5958]351 /**
352 * Adapter for MapFrame change events
353 */
354 private class MapFrameAdapter implements MapFrameListener {
355 private ListSelectionListener conflictSelectionListener;
356 private TreeSelectionListener validatorSelectionListener;
357
358 public MapFrameAdapter() {
[7012]359 if ("conflict".equals(mode)) {
[5958]360 conflictSelectionListener = new ListSelectionListener() {
[8171]361 @Override
362 public void valueChanged(ListSelectionEvent e) {
[5958]363 updateEnabledState();
364 }
365 };
[7012]366 } else if ("problem".equals(mode)) {
[5958]367 validatorSelectionListener = new TreeSelectionListener() {
[8171]368 @Override
369 public void valueChanged(TreeSelectionEvent e) {
[5958]370 updateEnabledState();
371 }
372 };
373 }
374 }
375
[8171]376 @Override
377 public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
[5958]378 if (conflictSelectionListener != null) {
379 if (newFrame != null) {
380 newFrame.conflictDialog.addListSelectionListener(conflictSelectionListener);
381 } else if (oldFrame != null) {
382 oldFrame.conflictDialog.removeListSelectionListener(conflictSelectionListener);
383 }
384 } else if (validatorSelectionListener != null) {
385 if (newFrame != null) {
386 newFrame.validatorDialog.addTreeSelectionListener(validatorSelectionListener);
387 } else if (oldFrame != null) {
388 oldFrame.validatorDialog.removeTreeSelectionListener(validatorSelectionListener);
389 }
390 }
391 }
392 }
[403]393}
Note: See TracBrowser for help on using the repository browser.