diff --git a/project/app/src/gms/java/org/owntracks/android/ui/map/MapLayerStyle.kt b/project/app/src/gms/java/org/owntracks/android/ui/map/MapLayerStyle.kt index 14093f1a50..e1b2c21da5 100644 --- a/project/app/src/gms/java/org/owntracks/android/ui/map/MapLayerStyle.kt +++ b/project/app/src/gms/java/org/owntracks/android/ui/map/MapLayerStyle.kt @@ -5,6 +5,10 @@ import kotlinx.serialization.Serializable import org.owntracks.android.R import org.owntracks.android.preferences.types.FromConfiguration import org.owntracks.android.ui.map.osm.OSMMapFragment +import org.osmdroid.tileprovider.tilesource.ITileSource +import org.osmdroid.tileprovider.tilesource.TileSourceFactory +import org.osmdroid.tileprovider.tilesource.XYTileSource +import org.osmdroid.util.MapTileIndex @Serializable enum class MapLayerStyle { @@ -13,10 +17,12 @@ enum class MapLayerStyle { GoogleMapSatellite, GoogleMapTerrain, OpenStreetMapNormal, - OpenStreetMapWikimedia; + OpenStreetMapWikimedia, + AmapVector, + AmapSatellite; fun isSameProviderAs(mapLayerStyle: MapLayerStyle): Boolean { - return setOf("GoogleMap", "OpenStreetMap").any { + return setOf("GoogleMap", "OpenStreetMap", "Amap").any { name.startsWith(it) && mapLayerStyle.name.startsWith(it) } } @@ -28,10 +34,24 @@ enum class MapLayerStyle { GoogleMapSatellite, GoogleMapTerrain -> GoogleMapFragment::class.java OpenStreetMapNormal, - OpenStreetMapWikimedia -> OSMMapFragment::class.java + OpenStreetMapWikimedia, + AmapVector, + AmapSatellite -> OSMMapFragment::class.java } } + fun getTileSource(): ITileSource = + when (this) { + OpenStreetMapNormal -> TileSourceFactory.MAPNIK + OpenStreetMapWikimedia -> TileSourceFactory.WIKIMEDIA + AmapVector -> AMAP_VECTOR + AmapSatellite -> AMAP_SATELLITE + GoogleMapDefault, + GoogleMapHybrid, + GoogleMapSatellite, + GoogleMapTerrain -> TileSourceFactory.MAPNIK + } + companion object { @JvmStatic @FromConfiguration @@ -40,10 +60,58 @@ enum class MapLayerStyle { } } +private val AMAP_VECTOR: ITileSource = + object : + XYTileSource( + "AmapVector", + 1, + 18, + 256, + ".png", + arrayOf( + "https://webrd01.is.autonavi.com/", + "https://webrd02.is.autonavi.com/", + "https://webrd03.is.autonavi.com/", + "https://webrd04.is.autonavi.com/")) { + override fun getTileURLString(pMapTileIndex: Long): String { + val z = MapTileIndex.getZoom(pMapTileIndex) + val x = MapTileIndex.getX(pMapTileIndex) + val y = MapTileIndex.getY(pMapTileIndex) + return "${getBaseUrl()}appmaptile?lang=zh_cn&size=1&scale=1&style=8&x=$x&y=$y&z=$z" + } + + override fun getCopyrightNotice(): String = "© Amap.com" + } + +private val AMAP_SATELLITE: ITileSource = + object : + XYTileSource( + "AmapSatellite", + 1, + 18, + 256, + ".jpg", + arrayOf( + "https://webst01.is.autonavi.com/", + "https://webst02.is.autonavi.com/", + "https://webst03.is.autonavi.com/", + "https://webst04.is.autonavi.com/")) { + override fun getTileURLString(pMapTileIndex: Long): String { + val z = MapTileIndex.getZoom(pMapTileIndex) + val x = MapTileIndex.getX(pMapTileIndex) + val y = MapTileIndex.getY(pMapTileIndex) + return "${getBaseUrl()}appmaptile?style=6&x=$x&y=$y&z=$z" + } + + override fun getCopyrightNotice(): String = "© Amap.com" + } + val mapLayerSelectorButtonsToStyles = mapOf( R.id.fabMapLayerGoogleNormal to MapLayerStyle.GoogleMapDefault, R.id.fabMapLayerGoogleHybrid to MapLayerStyle.GoogleMapHybrid, R.id.fabMapLayerGoogleTerrain to MapLayerStyle.GoogleMapTerrain, R.id.fabMapLayerOpenStreetMap to MapLayerStyle.OpenStreetMapNormal, - R.id.fabMapLayerOpenStreetMapWikimedia to MapLayerStyle.OpenStreetMapWikimedia) + R.id.fabMapLayerOpenStreetMapWikimedia to MapLayerStyle.OpenStreetMapWikimedia, + R.id.fabMapLayerAmapVector to MapLayerStyle.AmapVector, + R.id.fabMapLayerAmapSatellite to MapLayerStyle.AmapSatellite) \ No newline at end of file diff --git a/project/app/src/gms/res/layout/map_layer_bottom_sheet_dialog.xml b/project/app/src/gms/res/layout/map_layer_bottom_sheet_dialog.xml index 6c08a44bc3..08e7f583f6 100644 --- a/project/app/src/gms/res/layout/map_layer_bottom_sheet_dialog.xml +++ b/project/app/src/gms/res/layout/map_layer_bottom_sheet_dialog.xml @@ -148,6 +148,51 @@ android:textAlignment="center" /> + + + + + + + + + + + + + diff --git a/project/app/src/main/java/org/owntracks/android/ui/map/osm/OSMMapFragment.kt b/project/app/src/main/java/org/owntracks/android/ui/map/osm/OSMMapFragment.kt index 0c81af3b61..c48146820e 100644 --- a/project/app/src/main/java/org/owntracks/android/ui/map/osm/OSMMapFragment.kt +++ b/project/app/src/main/java/org/owntracks/android/ui/map/osm/OSMMapFragment.kt @@ -25,7 +25,6 @@ import org.osmdroid.events.DelayedMapListener import org.osmdroid.events.MapListener import org.osmdroid.events.ScrollEvent import org.osmdroid.events.ZoomEvent -import org.osmdroid.tileprovider.tilesource.TileSourceFactory import org.osmdroid.util.TileSystemWebMercator import org.osmdroid.views.CustomZoomButtonsController import org.osmdroid.views.MapView @@ -440,13 +439,7 @@ internal constructor( } override fun setMapLayerType(mapLayerStyle: MapLayerStyle) { - when (mapLayerStyle) { - MapLayerStyle.OpenStreetMapNormal -> - binding.osmMapView.setTileSource(TileSourceFactory.MAPNIK) - MapLayerStyle.OpenStreetMapWikimedia -> - binding.osmMapView.setTileSource(TileSourceFactory.WIKIMEDIA) - else -> Timber.w("Unsupported map layer type $mapLayerStyle") - } + binding.osmMapView.setTileSource(mapLayerStyle.getTileSource()) } fun createPolygon(mapView: MapView, waypoint: WaypointModel): Polygon { diff --git a/project/app/src/main/res/drawable/ic_amap_logo.xml b/project/app/src/main/res/drawable/ic_amap_logo.xml new file mode 100644 index 0000000000..cdc762084e --- /dev/null +++ b/project/app/src/main/res/drawable/ic_amap_logo.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/project/app/src/main/res/values-zh/strings.xml b/project/app/src/main/res/values-zh/strings.xml index dd4fd557d4..6faae36d23 100644 --- a/project/app/src/main/res/values-zh/strings.xml +++ b/project/app/src/main/res/values-zh/strings.xml @@ -108,6 +108,8 @@ "Google 地形" "OpenStreetMap" "维基媒体" + "高德地图" + "高德卫星图" "清除" "导航" "模式" diff --git a/project/app/src/main/res/values/strings.xml b/project/app/src/main/res/values/strings.xml index d182ffc3b9..54e5659d98 100644 --- a/project/app/src/main/res/values/strings.xml +++ b/project/app/src/main/res/values/strings.xml @@ -114,6 +114,8 @@ To allow this, please enable Location in the device settings." "Google Terrain" "OpenStreetMap" "Wikimedia" + "Amap" + "Amap Satellite" "Clear" "Navigate" "Request Location" diff --git a/project/app/src/oss/java/org/owntracks/android/ui/map/MapLayerStyle.kt b/project/app/src/oss/java/org/owntracks/android/ui/map/MapLayerStyle.kt index af2096acde..4211d406c9 100644 --- a/project/app/src/oss/java/org/owntracks/android/ui/map/MapLayerStyle.kt +++ b/project/app/src/oss/java/org/owntracks/android/ui/map/MapLayerStyle.kt @@ -3,23 +3,104 @@ package org.owntracks.android.ui.map import kotlinx.serialization.Serializable import org.owntracks.android.R import org.owntracks.android.preferences.types.FromConfiguration +import org.osmdroid.tileprovider.tilesource.ITileSource +import org.osmdroid.tileprovider.tilesource.TileSourceFactory +import org.osmdroid.tileprovider.tilesource.XYTileSource +import org.osmdroid.util.MapTileIndex @Serializable enum class MapLayerStyle { OpenStreetMapNormal, - OpenStreetMapWikimedia; + OpenStreetMapWikimedia, + AmapVector, + AmapSatellite; - fun isSameProviderAs(@Suppress("UNUSED_PARAMETER") mapLayerStyle: MapLayerStyle): Boolean = true + fun isSameProviderAs(mapLayerStyle: MapLayerStyle): Boolean { + return providerGroup() == mapLayerStyle.providerGroup() + } + + private fun providerGroup(): String = + when (this) { + OpenStreetMapNormal, + OpenStreetMapWikimedia -> "OpenStreetMap" + AmapVector, + AmapSatellite -> "Amap" + } + + fun getTileSource(): ITileSource = + when (this) { + OpenStreetMapNormal -> TileSourceFactory.MAPNIK + OpenStreetMapWikimedia -> TileSourceFactory.WIKIMEDIA + AmapVector -> AMAP_VECTOR + AmapSatellite -> AMAP_SATELLITE + } companion object { @JvmStatic @FromConfiguration fun getByValue(value: String): MapLayerStyle = - MapLayerStyle.values().firstOrNull { it.name.equals(value, true) } ?: OpenStreetMapNormal + entries.firstOrNull { it.name.equals(value, true) } ?: OpenStreetMapNormal } } +/** + * Amap (高德) vector tiles served over HTTPS without an API key. The URL pattern uses query + * string parameters rather than the standard `{z}/{x}/{y}.png` slippy map convention, so we + * subclass XYTileSource to override the URL builder. + */ +private val AMAP_VECTOR: ITileSource = + object : + XYTileSource( + "AmapVector", + 1, + 18, + 256, + ".png", + arrayOf( + "https://webrd01.is.autonavi.com/", + "https://webrd02.is.autonavi.com/", + "https://webrd03.is.autonavi.com/", + "https://webrd04.is.autonavi.com/")) { + override fun getTileURLString(pMapTileIndex: Long): String { + val z = MapTileIndex.getZoom(pMapTileIndex) + val x = MapTileIndex.getX(pMapTileIndex) + val y = MapTileIndex.getY(pMapTileIndex) + return "${getBaseUrl()}appmaptile?lang=zh_cn&size=1&scale=1&style=8&x=$x&y=$y&z=$z" + } + + override fun getCopyrightNotice(): String = "© Amap.com" + } + +/** + * Amap satellite imagery tiles (no API key required). Uses the `webst0X` subdomain (different + * from the vector layer's `webrd0X`) and the `style=6` parameter for raster imagery. + */ +private val AMAP_SATELLITE: ITileSource = + object : + XYTileSource( + "AmapSatellite", + 1, + 18, + 256, + ".jpg", + arrayOf( + "https://webst01.is.autonavi.com/", + "https://webst02.is.autonavi.com/", + "https://webst03.is.autonavi.com/", + "https://webst04.is.autonavi.com/")) { + override fun getTileURLString(pMapTileIndex: Long): String { + val z = MapTileIndex.getZoom(pMapTileIndex) + val x = MapTileIndex.getX(pMapTileIndex) + val y = MapTileIndex.getY(pMapTileIndex) + return "${getBaseUrl()}appmaptile?style=6&x=$x&y=$y&z=$z" + } + + override fun getCopyrightNotice(): String = "© Amap.com" + } + val mapLayerSelectorButtonsToStyles = mapOf( R.id.fabMapLayerOpenStreetMap to MapLayerStyle.OpenStreetMapNormal, - R.id.fabMapLayerOpenStreetMapWikimedia to MapLayerStyle.OpenStreetMapWikimedia) + R.id.fabMapLayerOpenStreetMapWikimedia to MapLayerStyle.OpenStreetMapWikimedia, + R.id.fabMapLayerAmapVector to MapLayerStyle.AmapVector, + R.id.fabMapLayerAmapSatellite to MapLayerStyle.AmapSatellite) \ No newline at end of file diff --git a/project/app/src/oss/res/layout/map_layer_bottom_sheet_dialog.xml b/project/app/src/oss/res/layout/map_layer_bottom_sheet_dialog.xml index bb4b7405c4..dc96bcf039 100644 --- a/project/app/src/oss/res/layout/map_layer_bottom_sheet_dialog.xml +++ b/project/app/src/oss/res/layout/map_layer_bottom_sheet_dialog.xml @@ -15,17 +15,18 @@ android:text="@string/mapLayerDialogTitle" android:textStyle="bold" /> - - + android:columnCount="2" + android:rowCount="2"> + + + + + + + + + + + + + - + - + \ No newline at end of file