Skip to content

Commit

Permalink
Merge branch 'panoramax_layer'
Browse files Browse the repository at this point in the history
  • Loading branch information
simonpoole committed Jan 15, 2025
2 parents 833aa54 + abd4bed commit 71376cb
Show file tree
Hide file tree
Showing 47 changed files with 1,803 additions and 720 deletions.
4 changes: 4 additions & 0 deletions documentation/docs/help/en/Advanced preferences.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,10 @@ Configure the overpass API server used online object search.

Configure the OpenAerialMap server used for providing additional imagery for backgrounds.

### Panoramax URL

The base URL for a Panoramax instance without the appended "/api".

## Layer download and storage

Download and storage configuration for the tiled imagery layers.
Expand Down
5 changes: 3 additions & 2 deletions documentation/docs/help/en/Main map display.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ The layer dialog supports the following actions on the layer entries:
* __Change style__ Show the layer styling dialog (disabled if a style has been loaded).
* __Load style__ Load a mapbox-gl style.
* __Reset style__ Reset the style to the default.
* Mapillary layer (additionally to _Mapbox Vector Tile layers_):
* __Set date range ...__ Select start and end date to filter Mapillary data on. Note that this functionality depends on some specifics of the loaded Mapbox GL style.
* Mapillary and Panoramax layers (additionally to _Mapbox Vector Tile layers_):
* __Set date range ...__ Select start and end date to filter the displayed data on. Note that this functionality depends on some specifics of the loaded Mapbox GL style.
* GeoJSON layers:
* __Change style__ Show the layer styling dialog.
* __Info__ Display some information on the contents.
Expand Down Expand Up @@ -103,6 +103,7 @@ The layer dialog supports the following actions on the layer entries:
* __Enable photo layer__ Enables the photo layer this will display clickable icons for photos that will start an internal or external viewer. Which photos can be displayed depends strongly on your Android version and settings [Advanced preferences](Advanced%20preferences.md).
* __Enable bookmark layer__ Enables a layer displaying saved bookmarks.
* __Enable Mapillary layer__ Enables the Mapillay layer.
* __Enable Panoramax layer__ Enables the Panoramax layer.
* __Add layer from GPX file__ Adds a layer from a GPX file on device.
* __Download GPX track__ Download a GPX [track that you have previously uploaded to the OSM website](https://www.openstreetmap.org/traces/mine), and create a layer from it. Note that only GPX tracks with their starting point in the area currently displayed will be available for selection, this is a limitation of the current OSM API.
* __Add custom imagery__ Adds a custom imagery configuration, this can then be used just
Expand Down
3 changes: 2 additions & 1 deletion documentation/docs/tutorials/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ This list is likely not really complete, but gives some idea of the supported fu
| | GPX track upload |
| | GPX waypoint support |
|__Street level imagery__ | |
| | Mapillary support |
| | Mapillary
| | Panoramax |
|__Misc__ | |
| | Styleable data layer |
| | Turn restriction rendering |
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.blau.android.layer.mapillary;
package de.blau.android.layer.streetlevel.mapillary;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
Expand Down Expand Up @@ -40,7 +40,8 @@
import de.blau.android.dialogs.DateRangeDialog;
import de.blau.android.layer.LayerDialogTest;
import de.blau.android.layer.LayerType;
import de.blau.android.photos.MapillaryViewerActivity;
import de.blau.android.layer.streetlevel.ImageViewerActivity;
import de.blau.android.layer.streetlevel.mapillary.MapillaryOverlay;
import de.blau.android.prefs.AdvancedPrefDatabase;
import de.blau.android.prefs.Preferences;
import de.blau.android.resources.TileLayerDatabase;
Expand Down Expand Up @@ -81,7 +82,7 @@ public void setup() {
TestUtils.grantPermissons(device);
LayerUtils.removeLayer(main, LayerType.MAPILLARY);
tileServer = MockTileServer.setupTileServer(main, "mapillary.mbt", true, LayerType.MAPILLARY, TileType.MVT,
de.blau.android.layer.mapillary.MapillaryOverlay.MAPILLARY_TILES_ID);
de.blau.android.layer.streetlevel.mapillary.MapillaryOverlay.MAPILLARY_TILES_ID);

mockApiServer = new MockWebServerPlus();
HttpUrl mockApiBaseUrl = mockApiServer.server().url("/");
Expand Down Expand Up @@ -120,7 +121,7 @@ public void teardown() {
}
LayerUtils.removeLayer(main, LayerType.MAPILLARY);
try (TileLayerDatabase tlDb = new TileLayerDatabase(main); SQLiteDatabase db = tlDb.getWritableDatabase()) {
TileLayerDatabase.deleteLayerWithId(db, de.blau.android.layer.mapillary.MapillaryOverlay.MAPILLARY_TILES_ID);
TileLayerDatabase.deleteLayerWithId(db, de.blau.android.layer.streetlevel.mapillary.MapillaryOverlay.MAPILLARY_TILES_ID);
}
instrumentation.waitForIdleSync();
}
Expand All @@ -135,7 +136,7 @@ public void mapillaryLayer() {
imageResponse.setResponseCode(200);
imageResponse.setBody("{\"thumb_2048_url\": \"" + mockImagesBaseUrl.toString() + "\",\"computed_geometry\": {\"type\": \"Point\",\"coordinates\": ["
+ "8.407748800863,47.412813485744]" + "},\"id\": \"178993950747668\"}");
de.blau.android.layer.mapillary.MapillaryOverlay layer = (MapillaryOverlay) map.getLayer(LayerType.MAPILLARY);
de.blau.android.layer.streetlevel.mapillary.MapillaryOverlay layer = (MapillaryOverlay) map.getLayer(LayerType.MAPILLARY);
assertNotNull(layer);
layer.flushCaches(main); // forces the layer to retrieve everything

Expand All @@ -154,7 +155,7 @@ public void mapillaryLayer() {
TestUtils.unlock(device);
TestUtils.sleep();

ActivityMonitor monitor = instrumentation.addMonitor(MapillaryViewerActivity.class.getName(), null, false);
ActivityMonitor monitor = instrumentation.addMonitor(ImageViewerActivity.class.getName(), null, false);
// hack around slow rendering on some emulators
map.getViewBox().moveTo(map, (int) (8.407748800863 * 1E7), (int) (47.412813485744 * 1E7));
map.invalidate();
Expand All @@ -163,9 +164,9 @@ public void mapillaryLayer() {
if (TestUtils.clickText(device, false, "OK", true)) {
TestUtils.clickAtCoordinates(device, map, 8.407748800863, 47.412813485744, true);
}
MapillaryViewerActivity viewer = null;
ImageViewerActivity viewer = null;
try {
viewer = (MapillaryViewerActivity) instrumentation.waitForMonitorWithTimeout(monitor, 30000);
viewer = (ImageViewerActivity) instrumentation.waitForMonitorWithTimeout(monitor, 30000);
assertNotNull(viewer);
try {
RecordedRequest recorded = mockImagesServer.server().takeRequest(10, TimeUnit.SECONDS);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* Mapillary related testing
*/
package de.blau.android.layer.streetlevel.mapillary;
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package de.blau.android.layer.streetlevel.panoramax;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import com.orhanobut.mockwebserverplus.MockWebServerPlus;

import android.app.Instrumentation;
import android.app.Instrumentation.ActivityMonitor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObject2;
import de.blau.android.App;
import de.blau.android.LayerUtils;
import de.blau.android.Logic;
import de.blau.android.Main;
import de.blau.android.Map;
import de.blau.android.MockTileServer;
import de.blau.android.R;
import de.blau.android.TestUtils;
import de.blau.android.dialogs.DateRangeDialog;
import de.blau.android.layer.LayerDialogTest;
import de.blau.android.layer.LayerType;
import de.blau.android.layer.streetlevel.ImageViewerActivity;
import de.blau.android.layer.streetlevel.mapillary.MapillaryOverlay;
import de.blau.android.prefs.AdvancedPrefDatabase;
import de.blau.android.prefs.Preferences;
import de.blau.android.resources.TileLayerDatabase;
import de.blau.android.resources.TileLayerSource.TileType;
import okhttp3.HttpUrl;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;

@RunWith(AndroidJUnit4.class)
@LargeTest
public class PanoramaxTest {

AdvancedPrefDatabase prefDB = null;
Main main = null;
UiDevice device = null;
Map map = null;
Logic logic = null;
Instrumentation instrumentation = null;
MockWebServerPlus mockApiServer = null;
MockWebServer tileServer = null;
HttpUrl mockImagesBaseUrl = null;

@Rule
public ActivityTestRule<Main> mActivityRule = new ActivityTestRule<>(Main.class);

/**
* Pre-test setup
*/
@Before
public void setup() {
instrumentation = InstrumentationRegistry.getInstrumentation();
device = UiDevice.getInstance(instrumentation);
main = mActivityRule.getActivity();

assertNotNull(main);
TestUtils.grantPermissons(device);
LayerUtils.removeLayer(main, LayerType.PANORAMAX);
tileServer = MockTileServer.setupTileServer(main, "panoramax.mbtiles", true, LayerType.PANORAMAX, TileType.MVT,
de.blau.android.layer.streetlevel.panoramax.PanoramaxOverlay.PANORAMAX_TILES_ID);

mockApiServer = new MockWebServerPlus();
HttpUrl mockApiBaseUrl = mockApiServer.server().url("/");
Preferences prefs = new Preferences(main);
prefs.setPanoramaxApiUrl(mockApiBaseUrl.toString());

App.getLogic().setPrefs(prefs);
map = main.getMap();
map.setPrefs(main, prefs);

TestUtils.dismissStartUpDialogs(device, main);
TestUtils.loadTestData(main, "test2.osm");
map.getDataLayer().setVisible(true);
TestUtils.stopEasyEdit(main);
}

/**
* Post-test teardown
*/
@After
public void teardown() {
try {
mockApiServer.server().shutdown();
} catch (IOException e) {
// Ignore
}
try {
tileServer.close();
} catch (IOException | NullPointerException e) {
// ignore
}
LayerUtils.removeLayer(main, LayerType.MAPILLARY);
try (TileLayerDatabase tlDb = new TileLayerDatabase(main); SQLiteDatabase db = tlDb.getWritableDatabase()) {
TileLayerDatabase.deleteLayerWithId(db, de.blau.android.layer.streetlevel.mapillary.MapillaryOverlay.MAPILLARY_TILES_ID);
}
instrumentation.waitForIdleSync();
}

/**
* Add Panoramax layer and click on one image
*/
@Test
public void panoramaxLayer() {
mockApiServer.enqueue("panoramax_sequences");
de.blau.android.layer.streetlevel.panoramax.PanoramaxOverlay layer = (PanoramaxOverlay) map.getLayer(LayerType.PANORAMAX);
assertNotNull(layer);
layer.flushCaches(main); // forces the layer to retrieve everything

MockResponse image = TestUtils.createBinaryReponse("image/jpeg", "fixtures/mapillary_image_v4.jpg");
mockApiServer.server().enqueue(image);
mockApiServer.server().enqueue(image);
mockApiServer.server().enqueue(image);
mockApiServer.server().enqueue(image);
mockApiServer.server().enqueue(image);

TestUtils.unlock(device);
TestUtils.sleep();

ActivityMonitor monitor = instrumentation.addMonitor(ImageViewerActivity.class.getName(), null, false);
// hack around slow rendering on some emulators
map.getViewBox().moveTo(map, (int) (2.3281776 * 1E7), (int) (48.8698124 * 1E7));
map.invalidate();
TestUtils.zoomToLevel(device, main, 22);
TestUtils.clickAtCoordinates(device, map, 2.3281776, 48.8698124, true);
if (TestUtils.clickText(device, false, "OK", true)) {
TestUtils.clickAtCoordinates(device, map, 2.3281776, 48.8698124, true);
}
ImageViewerActivity viewer = null;
try {
viewer = (ImageViewerActivity) instrumentation.waitForMonitorWithTimeout(monitor, 30000);
assertNotNull(viewer);
try {
RecordedRequest recorded = mockApiServer.server().takeRequest(10, TimeUnit.SECONDS);
assertNotNull(recorded);
System.out.println(recorded.getPath());
mockApiServer.server().takeRequest(10, TimeUnit.SECONDS);
mockApiServer.server().takeRequest(10, TimeUnit.SECONDS);
mockApiServer.server().takeRequest(10, TimeUnit.SECONDS);
mockApiServer.server().takeRequest(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
fail(e.getMessage());
}
assertTrue(TestUtils.clickMenuButton(device, main.getString(R.string.share), false, true));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
assertTrue(TestUtils.findText(device, false, "178993950747668"));
} else { // currently can't test this properly on Android before 10
assertTrue(TestUtils.findText(device, false, "Share with"));
}
device.pressBack();
} finally {
instrumentation.removeMonitor(monitor);
if (viewer != null) {
viewer.finish();
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package de.blau.android.layer.streetlevel.panoramax;
Binary file added src/androidTest/resources/panoramax.mbtiles
Binary file not shown.
4 changes: 2 additions & 2 deletions src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -285,12 +285,12 @@
</intent-filter>
</activity>
<activity
android:name=".photos.MapillaryViewerActivity"
android:name=".layer.streetlevel.ImageViewerActivity"
android:configChanges="orientation|screenSize|keyboardHidden|density|screenLayout|uiMode|fontScale"
android:exported="true"
android:label="@string/mapillary_viewer_label"
android:launchMode="singleTask"
android:taskAffinity=".MapillaryViewerActivity"
android:taskAffinity=".ImageViewerActivity"
android:theme="@style/Theme.AppCompat.NoActionBar" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
2 changes: 2 additions & 0 deletions src/main/assets/help/en/Advanced preferences.html
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ <h3>Overpass server</h3>
<p>Configure the overpass API server used online object search.</p>
<h3>OpenAerialMap server</h3>
<p>Configure the OpenAerialMap server used for providing additional imagery for backgrounds.</p>
<h3>Panoramax URL</h3>
<p>The base URL for a Panoramax instance without the appended &quot;/api&quot;.</p>
<h2>Layer download and storage</h2>
<p>Download and storage configuration for the tiled imagery layers.</p>
<h3>Max. number of download threads</h3>
Expand Down
5 changes: 3 additions & 2 deletions src/main/assets/help/en/Main map display.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ <h3>Layer control</h3>
<li><strong>Reset style</strong> Reset the style to the default.</li>
</ul>
</li>
<li>Mapillary layer (additionally to <em>Mapbox Vector Tile layers</em>):
<li>Mapillary and Panoramax layers (additionally to <em>Mapbox Vector Tile layers</em>):
<ul>
<li><strong>Set date range ...</strong> Select start and end date to filter Mapillary data on. Note that this functionality depends on some specifics of the loaded Mapbox GL style.</li>
<li><strong>Set date range ...</strong> Select start and end date to filter the displayed data on. Note that this functionality depends on some specifics of the loaded Mapbox GL style.</li>
</ul>
</li>
<li>GeoJSON layers:
Expand Down Expand Up @@ -128,6 +128,7 @@ <h3>Layer control</h3>
<li><strong>Enable photo layer</strong> Enables the photo layer this will display clickable icons for photos that will start an internal or external viewer. Which photos can be displayed depends strongly on your Android version and settings <a href="Advanced%20preferences.md">Advanced preferences</a>.</li>
<li><strong>Enable bookmark layer</strong> Enables a layer displaying saved bookmarks.</li>
<li><strong>Enable Mapillary layer</strong> Enables the Mapillay layer.</li>
<li><strong>Enable Panoramax layer</strong> Enables the Panoramax layer.</li>
<li><strong>Add layer from GPX file</strong> Adds a layer from a GPX file on device.</li>
<li><strong>Download GPX track</strong> Download a GPX <a href="https://www.openstreetmap.org/traces/mine">track that you have previously uploaded to the OSM website</a>, and create a layer from it. Note that only GPX tracks with their starting point in the area currently displayed will be available for selection, this is a limitation of the current OSM API.</li>
<li><strong>Add custom imagery</strong> Adds a custom imagery configuration, this can then be used just as any tile based imagery source, the entries can be managed in the <a href="Preferences.md">preferences</a>.</li>
Expand Down
1 change: 0 additions & 1 deletion src/main/assets/imagery.geojson

This file was deleted.

14 changes: 14 additions & 0 deletions src/main/assets/imagery/imagery_vespucci.geojson
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,20 @@
"url": "https://tiles.mapillary.com/maps/vtp/mly1_computed_public/2/{z}/{x}/{y}?access_token={apikey}"
},
"type": "Feature"
},
{
"geometry": null,
"properties": {
"id": "PANORAMAX",
"overlay": true,
"max_zoom": 15,
"name": "Panoramax",
"type": "tms",
"tile_type": "mvt",
"category": "internal",
"url": "https://panoramax.openstreetmap.fr/api/map/{z}/{x}/{y}.pbf"
},
"type": "Feature"
}
],
"type": "FeatureCollection"
Expand Down
Loading

0 comments on commit 71376cb

Please sign in to comment.