From 19ccf18472593b6e653888e370a0313134e441b1 Mon Sep 17 00:00:00 2001
From: Robert Scott <code@humanleg.org.uk>
Date: Sun, 30 Sep 2018 12:33:27 +0100
Subject: [PATCH v1 2/4] SlippyMapBBoxChooser: redesign getAllTileSources to
perform deduplication of provided tile sources
this also enables us to easily ensure currently active TileSource remains present & selected in
the source menu even when it's no longer supplied by TileSourceProviders
---
.../josm/gui/bbox/SlippyMapBBoxChooser.java | 45 +++++++++++++---------
.../josm/gui/dialogs/MinimapDialogTest.java | 35 +++++++++++++++++
2 files changed, 61 insertions(+), 19 deletions(-)
diff --git a/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java b/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java
index 907a5152f..4b322854d 100644
a
|
b
|
import java.util.Arrays;
|
16 | 16 | import java.util.Collections; |
17 | 17 | import java.util.HashMap; |
18 | 18 | import java.util.HashSet; |
| 19 | import java.util.LinkedHashMap; |
19 | 20 | import java.util.List; |
20 | 21 | import java.util.Map; |
21 | 22 | import java.util.Set; |
22 | 23 | import java.util.concurrent.CopyOnWriteArrayList; |
23 | 24 | import java.util.concurrent.TimeUnit; |
| 25 | import java.util.stream.Collectors; |
24 | 26 | |
25 | 27 | import javax.swing.ButtonModel; |
26 | 28 | import javax.swing.JOptionPane; |
… |
… |
public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, Cha
|
78 | 80 | * TMS TileSource provider for the slippymap chooser |
79 | 81 | */ |
80 | 82 | public static class TMSTileSourceProvider implements TileSourceProvider { |
81 | | private static final Set<String> existingSlippyMapUrls = new HashSet<>(); |
82 | | static { |
83 | | // Urls that already exist in the slippymap chooser and shouldn't be copied from TMS layer list |
84 | | existingSlippyMapUrls.add("https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png"); // Mapnik |
85 | | } |
86 | 83 | |
87 | 84 | @Override |
88 | 85 | public List<TileSource> getTileSources() { |
89 | 86 | if (!TMSLayer.PROP_ADD_TO_SLIPPYMAP_CHOOSER.get()) return Collections.<TileSource>emptyList(); |
90 | 87 | List<TileSource> sources = new ArrayList<>(); |
91 | 88 | for (ImageryInfo info : ImageryLayerInfo.instance.getLayers()) { |
92 | | if (existingSlippyMapUrls.contains(info.getUrl())) { |
93 | | continue; |
94 | | } |
95 | 89 | try { |
96 | 90 | TileSource source = TMSLayer.getTileSourceStatic(info); |
97 | 91 | if (source != null) { |
… |
… |
public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, Cha
|
177 | 171 | } |
178 | 172 | setMaxTilesInMemory(Config.getPref().getInt("slippy_map_chooser.max_tiles", 1000)); |
179 | 173 | |
180 | | List<TileSource> tileSources = getAllTileSources(); |
| 174 | List<TileSource> tileSources = new ArrayList<>(getAllTileSources().values()); |
181 | 175 | |
182 | 176 | this.showDownloadAreaButtonModel = new JToggleButton.ToggleButtonModel(); |
183 | 177 | this.showDownloadAreaButtonModel.setSelected(PROP_SHOWDLAREA.get()); |
… |
… |
public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, Cha
|
210 | 204 | new SlippyMapControler(this, this); |
211 | 205 | } |
212 | 206 | |
213 | | private static List<TileSource> getAllTileSources() { |
214 | | List<TileSource> tileSources = new ArrayList<>(); |
215 | | for (TileSourceProvider provider: providers) { |
216 | | tileSources.addAll(provider.getTileSources()); |
217 | | } |
218 | | return tileSources; |
| 207 | private static LinkedHashMap<String, TileSource> getAllTileSources() { |
| 208 | // using a LinkedHashMap of <id, TileSource> to retain ordering but provide deduplication |
| 209 | return providers.stream().flatMap( |
| 210 | provider -> provider.getTileSources().stream() |
| 211 | ).collect(Collectors.toMap( |
| 212 | TileSource::getId, |
| 213 | ts -> ts, |
| 214 | (old, _new) -> old, |
| 215 | () -> new LinkedHashMap<String, TileSource>() |
| 216 | )); |
219 | 217 | } |
220 | 218 | |
221 | 219 | /** |
… |
… |
public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, Cha
|
358 | 356 | this.tileController.setTileCache(new MemoryTileCache()); |
359 | 357 | this.setTileSource(tileSource); |
360 | 358 | PROP_MAPSTYLE.put(tileSource.getName()); // TODO Is name really unique? |
361 | | if (this.iSourceButton.getCurrentSource() != tileSource) { // prevent infinite recursion |
362 | | this.iSourceButton.setCurrentMap(tileSource); |
363 | | } |
| 359 | |
| 360 | // we need to refresh the tile sources in case the deselected source should no longer be present |
| 361 | // (and only remained there because its removal was deferred while the source was still the |
| 362 | // selected one). this should also have the effect of propagating the new selection to the |
| 363 | // iSourceButton & menu: it attempts to re-select the current source when rebuilding its menu. |
| 364 | this.refreshTileSources(); |
364 | 365 | } |
365 | 366 | |
366 | 367 | @Override |
… |
… |
public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser, Cha
|
416 | 417 | * @since 6364 |
417 | 418 | */ |
418 | 419 | public final void refreshTileSources() { |
419 | | iSourceButton.setSources(getAllTileSources()); |
| 420 | final LinkedHashMap<String, TileSource> newTileSources = getAllTileSources(); |
| 421 | final TileSource currentTileSource = this.getTileController().getTileSource(); |
| 422 | |
| 423 | // re-add the currently active TileSource to prevent inconsistent display of menu |
| 424 | newTileSources.putIfAbsent(currentTileSource.getId(), currentTileSource); |
| 425 | |
| 426 | this.iSourceButton.setSources(new ArrayList<>(newTileSources.values())); |
420 | 427 | } |
421 | 428 | } |
diff --git a/test/unit/org/openstreetmap/josm/gui/dialogs/MinimapDialogTest.java b/test/unit/org/openstreetmap/josm/gui/dialogs/MinimapDialogTest.java
index f8a61a41b..9d9fa63e3 100644
a
|
b
|
import org.openstreetmap.josm.TestUtils;
|
30 | 30 | import org.openstreetmap.josm.data.Bounds; |
31 | 31 | import org.openstreetmap.josm.data.DataSource; |
32 | 32 | import org.openstreetmap.josm.data.osm.DataSet; |
| 33 | import org.openstreetmap.josm.data.imagery.ImageryLayerInfo; |
33 | 34 | import org.openstreetmap.josm.data.projection.ProjectionRegistry; |
34 | 35 | import org.openstreetmap.josm.data.projection.Projections; |
35 | 36 | import org.openstreetmap.josm.gui.MainApplication; |
… |
… |
public class MinimapDialogTest {
|
257 | 258 | } |
258 | 259 | |
259 | 260 | /** |
| 261 | * Tests that the currently selected source being removed from ImageryLayerInfo will remain present and |
| 262 | * selected in the source menu even after the tile sources have been refreshed. |
| 263 | * @throws Exception if any error occurs |
| 264 | */ |
| 265 | @Test |
| 266 | public void testRemovedSourceStillSelected() throws Exception { |
| 267 | // relevant prefs starting out empty, should choose the first source and have shown download area enabled |
| 268 | // (not that there's a data layer for it to use) |
| 269 | |
| 270 | this.setUpMiniMap(); |
| 271 | |
| 272 | this.clickSourceMenuItemByLabel("Green Tiles"); |
| 273 | |
| 274 | ImageryLayerInfo.instance.remove( |
| 275 | ImageryLayerInfo.instance.getLayers().stream().filter(i -> i.getName().equals("Green Tiles")).findAny().get() |
| 276 | ); |
| 277 | |
| 278 | this.assertSingleSelectedSourceLabel("Green Tiles"); |
| 279 | |
| 280 | this.slippyMap.refreshTileSources(); |
| 281 | |
| 282 | this.assertSingleSelectedSourceLabel("Green Tiles"); |
| 283 | |
| 284 | // call paint to trigger new tile fetch |
| 285 | this.paintSlippyMap(); |
| 286 | |
| 287 | Awaitility.await().atMost(1000, MILLISECONDS).until(this.slippyMapTasksFinished); |
| 288 | |
| 289 | this.paintSlippyMap(); |
| 290 | |
| 291 | assertEquals(0xff00ff00, paintedSlippyMap.getRGB(0, 0)); |
| 292 | } |
| 293 | |
| 294 | /** |
260 | 295 | * Tests minimap obeys a saved "mapstyle" preference on startup. |
261 | 296 | * @throws Exception if any error occurs |
262 | 297 | */ |