| | 1 | // License: GPL. For details, see LICENSE file. |
| | 2 | package org.openstreetmap.josm.gui.layer.imagery; |
| | 3 | |
| | 4 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; |
| | 5 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; |
| | 6 | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | 7 | import static org.junit.jupiter.api.Assertions.assertFalse; |
| | 8 | import static org.junit.jupiter.api.Assertions.assertNotNull; |
| | 9 | import static org.junit.jupiter.api.Assertions.assertTrue; |
| | 10 | |
| | 11 | import java.util.Collections; |
| | 12 | |
| | 13 | import org.apache.commons.jcs3.access.behavior.ICacheAccess; |
| | 14 | import org.awaitility.Awaitility; |
| | 15 | import org.awaitility.Durations; |
| | 16 | import org.junit.jupiter.api.BeforeEach; |
| | 17 | import org.junit.jupiter.api.Test; |
| | 18 | import org.junit.jupiter.api.extension.RegisterExtension; |
| | 19 | import org.junit.jupiter.params.ParameterizedTest; |
| | 20 | import org.junit.jupiter.params.provider.ValueSource; |
| | 21 | import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener; |
| | 22 | import org.openstreetmap.josm.TestUtils; |
| | 23 | import org.openstreetmap.josm.actions.ExpertToggleAction; |
| | 24 | import org.openstreetmap.josm.data.Bounds; |
| | 25 | import org.openstreetmap.josm.data.imagery.ImageryInfo; |
| | 26 | import org.openstreetmap.josm.data.imagery.TileJobOptions; |
| | 27 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MVTFile; |
| | 28 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MVTTile; |
| | 29 | import org.openstreetmap.josm.data.imagery.vectortile.mapbox.MapboxVectorCachedTileLoader; |
| | 30 | import org.openstreetmap.josm.data.osm.BBox; |
| | 31 | import org.openstreetmap.josm.data.projection.Projection; |
| | 32 | import org.openstreetmap.josm.data.projection.ProjectionRegistry; |
| | 33 | import org.openstreetmap.josm.data.projection.Projections; |
| | 34 | import org.openstreetmap.josm.gui.MainApplication; |
| | 35 | import org.openstreetmap.josm.testutils.FakeGraphics; |
| | 36 | import org.openstreetmap.josm.testutils.JOSMTestRules; |
| | 37 | import org.openstreetmap.josm.testutils.annotations.BasicPreferences; |
| | 38 | |
| | 39 | /** |
| | 40 | * Test class for {@link MVTLayer} |
| | 41 | */ |
| | 42 | @BasicPreferences |
| | 43 | class MVTLayerTest { |
| | 44 | // Needed for setting HTTP factory and the main window/mapview |
| | 45 | @RegisterExtension |
| | 46 | JOSMTestRules josmTestRules = new JOSMTestRules().main().projection(); |
| | 47 | |
| | 48 | MVTLayer testLayer; |
| | 49 | |
| | 50 | @BeforeEach |
| | 51 | void setUp() { |
| | 52 | final ImageryInfo imageryInfo = new ImageryInfo("MvtLayerTest", "file:" + TestUtils.getTestDataRoot() + "pbf/mapillary/{z}/{x}/{y}.mvt"); |
| | 53 | imageryInfo.setImageryType(ImageryInfo.ImageryType.MVT); |
| | 54 | this.testLayer = new MVTLayer(imageryInfo); |
| | 55 | } |
| | 56 | |
| | 57 | @Test |
| | 58 | void getTileLoaderClass() { |
| | 59 | assertEquals(MapboxVectorCachedTileLoader.class, this.testLayer.getTileLoaderClass()); |
| | 60 | } |
| | 61 | |
| | 62 | @Test |
| | 63 | void getCacheName() { |
| | 64 | assertEquals("MVT", this.testLayer.getCacheName()); |
| | 65 | } |
| | 66 | |
| | 67 | @Test |
| | 68 | void getCache() { |
| | 69 | assertNotNull(MVTLayer.getCache()); |
| | 70 | } |
| | 71 | |
| | 72 | @Test |
| | 73 | void getNativeProjections() { |
| | 74 | assertArrayEquals(Collections.singleton(MVTFile.DEFAULT_PROJECTION).toArray(), this.testLayer.getNativeProjections().toArray()); |
| | 75 | } |
| | 76 | |
| | 77 | /** |
| | 78 | * This is a non-regression test for JOSM #21260 |
| | 79 | * @param projectionCode The projection code to use |
| | 80 | * @throws ReflectiveOperationException If the required method was unable to be called |
| | 81 | */ |
| | 82 | @ParameterizedTest |
| | 83 | @ValueSource(strings = {"EPSG:3857" /* WGS 84 */, "EPSG:4326" /* Mercator (default) */, "EPSG:32612" /* UTM 12 N */}) |
| | 84 | void ensureDifferentProjectionsAreFetched(final String projectionCode) throws ReflectiveOperationException { |
| | 85 | final Projection originalProjection = ProjectionRegistry.getProjection(); |
| | 86 | try { |
| | 87 | ProjectionRegistry.setProjection(Projections.getProjectionByCode(projectionCode)); |
| | 88 | // Needed to initialize mapView |
| | 89 | MainApplication.getLayerManager().addLayer(this.testLayer); |
| | 90 | final BBox tileBBox = new MVTTile(this.testLayer.getTileSource(), 3251, 6258, 14).getBBox(); |
| | 91 | MainApplication.getMap().mapView.zoomTo(new Bounds(tileBBox.getMinLat(), tileBBox.getMinLon(), |
| | 92 | tileBBox.getMaxLat(), tileBBox.getMaxLon())); |
| | 93 | final FakeGraphics graphics2D = new FakeGraphics(); |
| | 94 | graphics2D.setClip(0, 0, 100, 100); |
| | 95 | this.testLayer.setZoomLevel(14); |
| | 96 | this.testLayer.getDisplaySettings().setAutoZoom(false); |
| | 97 | MainApplication.getMap().mapView.paintLayer(this.testLayer, graphics2D); |
| | 98 | Awaitility.await().atMost(Durations.ONE_SECOND).until(() -> !this.testLayer.getData().allPrimitives().isEmpty()); |
| | 99 | assertFalse(this.testLayer.getData().allPrimitives().isEmpty()); |
| | 100 | } finally { |
| | 101 | ProjectionRegistry.setProjection(originalProjection); |
| | 102 | } |
| | 103 | } |
| | 104 | |
| | 105 | @Test |
| | 106 | void getTileSource() { |
| | 107 | assertEquals(this.testLayer.getInfo().getUrl(), this.testLayer.getTileSource().getBaseUrl()); |
| | 108 | } |
| | 109 | |
| | 110 | @Test |
| | 111 | void createTile() { |
| | 112 | assertNotNull(this.testLayer.createTile(this.testLayer.getTileSource(), 3251, 6258, 14)); |
| | 113 | } |
| | 114 | |
| | 115 | @ParameterizedTest |
| | 116 | @ValueSource(booleans = {true, false}) |
| | 117 | void getMenuEntries(final boolean isExpert) { |
| | 118 | ExpertToggleAction.getInstance().setExpert(isExpert); |
| | 119 | // For now, just ensure that nothing throws on implementation |
| | 120 | MainApplication.getLayerManager().addLayer(this.testLayer); |
| | 121 | assertNotNull(assertDoesNotThrow(() -> this.testLayer.getMenuEntries())); |
| | 122 | } |
| | 123 | |
| | 124 | @Test |
| | 125 | void getData() { |
| | 126 | assertNotNull(this.testLayer.getData()); |
| | 127 | } |
| | 128 | |
| | 129 | @Test |
| | 130 | void finishedLoading() throws ReflectiveOperationException { |
| | 131 | final MVTTile mvtTile = (MVTTile) this.testLayer.createTile(this.testLayer.getTileSource(), 3251, 6258, 14); |
| | 132 | final FinishedLoading finishedLoading = new FinishedLoading(); |
| | 133 | mvtTile.addTileLoaderFinisher(finishedLoading); |
| | 134 | assertTrue(this.testLayer.getData().allPrimitives().isEmpty()); |
| | 135 | this.testLayer.getTileLoaderClass().getConstructor(TileLoaderListener.class, ICacheAccess.class, TileJobOptions.class) |
| | 136 | .newInstance(this.testLayer, MVTLayer.getCache(), new TileJobOptions(50, 50, Collections.emptyMap(), 1)) |
| | 137 | .createTileLoaderJob(mvtTile).submit(); |
| | 138 | Awaitility.await().atMost(Durations.ONE_SECOND).until(() -> finishedLoading.finished); |
| | 139 | assertFalse(this.testLayer.getData().allPrimitives().isEmpty()); |
| | 140 | } |
| | 141 | |
| | 142 | /** |
| | 143 | * For some reason, lambdas get garbage collected by WeakReference's. This avoids that. |
| | 144 | */ |
| | 145 | private static final class FinishedLoading implements MVTTile.TileListener { |
| | 146 | boolean finished; |
| | 147 | @Override |
| | 148 | public void finishedLoading(MVTTile tile) { |
| | 149 | this.finished = true; |
| | 150 | } |
| | 151 | } |
| | 152 | } |