144 lines
5.8 KiB
Kotlin
144 lines
5.8 KiB
Kotlin
package de.waypointaudio.data
|
||
|
||
import android.content.Context
|
||
import androidx.datastore.preferences.core.edit
|
||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||
import androidx.datastore.preferences.preferencesDataStore
|
||
import kotlinx.coroutines.flow.Flow
|
||
import kotlinx.coroutines.flow.first
|
||
import kotlinx.coroutines.flow.map
|
||
import org.osmdroid.tileprovider.tilesource.ITileSource
|
||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||
import org.osmdroid.tileprovider.tilesource.XYTileSource
|
||
import org.osmdroid.util.MapTileIndex
|
||
|
||
/**
|
||
* Verfügbare Karten-Anbieter (Phase 1 von v2.0.0).
|
||
*
|
||
* Alle Provider werden via [org.osmdroid.tileprovider.tilesource.XYTileSource]
|
||
* angebunden — kein Library-Wechsel nötig. Die OSM-Attribution
|
||
* (siehe `osm_attribution_short`) bleibt für sämtliche Provider sichtbar.
|
||
*
|
||
* Hinweise zu Provider-spezifischen Bedingungen:
|
||
* - **CartoDB** Tiles dürfen für nicht-kommerzielle Nutzung frei verwendet
|
||
* werden (Attribution `© OpenStreetMap & CartoDB`).
|
||
* - **OpenTopoMap** ist für nicht-kommerzielle Nutzung frei.
|
||
* - **ESRI** Tiles sind für persönliche, nicht-kommerzielle Nutzung
|
||
* via Attribution `© Esri` erlaubt.
|
||
* - **Stadia Maps** verlangt einen API-Key für produktive Nutzung; der Default
|
||
* funktioniert für Tests; im Zweifel CartoDB/OSM verwenden.
|
||
*/
|
||
enum class MapProvider(
|
||
val key: String,
|
||
val displayNameRes: Int
|
||
) {
|
||
OSM("osm", de.waypointaudio.R.string.map_provider_osm),
|
||
CARTO_POSITRON("carto_positron", de.waypointaudio.R.string.map_provider_carto_positron),
|
||
CARTO_DARK("carto_dark", de.waypointaudio.R.string.map_provider_carto_dark),
|
||
OPEN_TOPO("open_topo", de.waypointaudio.R.string.map_provider_topo),
|
||
ESRI_SATELLITE("esri_satellite", de.waypointaudio.R.string.map_provider_esri_satellite),
|
||
ESRI_HYBRID("esri_hybrid", de.waypointaudio.R.string.map_provider_esri_hybrid),
|
||
STADIA_ALIDADE("stadia_alidade", de.waypointaudio.R.string.map_provider_stadia_alidade),
|
||
STADIA_OUTDOORS("stadia_outdoors", de.waypointaudio.R.string.map_provider_stadia_outdoors);
|
||
|
||
fun toTileSource(): ITileSource = when (this) {
|
||
OSM -> TileSourceFactory.MAPNIK
|
||
CARTO_POSITRON -> XYTileSource(
|
||
"CartoDB Positron", 1, 19, 256, ".png",
|
||
arrayOf(
|
||
"https://a.basemaps.cartocdn.com/light_all/",
|
||
"https://b.basemaps.cartocdn.com/light_all/",
|
||
"https://c.basemaps.cartocdn.com/light_all/"
|
||
),
|
||
"© OpenStreetMap-Mitwirkende, © CARTO"
|
||
)
|
||
CARTO_DARK -> XYTileSource(
|
||
"CartoDB Dark Matter", 1, 19, 256, ".png",
|
||
arrayOf(
|
||
"https://a.basemaps.cartocdn.com/dark_all/",
|
||
"https://b.basemaps.cartocdn.com/dark_all/",
|
||
"https://c.basemaps.cartocdn.com/dark_all/"
|
||
),
|
||
"© OpenStreetMap-Mitwirkende, © CARTO"
|
||
)
|
||
OPEN_TOPO -> XYTileSource(
|
||
"OpenTopoMap", 1, 17, 256, ".png",
|
||
arrayOf(
|
||
"https://a.tile.opentopomap.org/",
|
||
"https://b.tile.opentopomap.org/",
|
||
"https://c.tile.opentopomap.org/"
|
||
),
|
||
"Karte: © OpenTopoMap (CC-BY-SA)"
|
||
)
|
||
ESRI_SATELLITE -> object : XYTileSource(
|
||
"ESRI World Imagery", 1, 19, 256, "",
|
||
arrayOf("https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/"),
|
||
"Tiles © Esri"
|
||
) {
|
||
override fun getTileURLString(pMapTileIndex: Long): String {
|
||
return baseUrl +
|
||
MapTileIndex.getZoom(pMapTileIndex) + "/" +
|
||
MapTileIndex.getY(pMapTileIndex) + "/" +
|
||
MapTileIndex.getX(pMapTileIndex)
|
||
}
|
||
}
|
||
ESRI_HYBRID -> object : XYTileSource(
|
||
"ESRI Hybrid Reference", 1, 19, 256, "",
|
||
arrayOf("https://server.arcgisonline.com/ArcGIS/rest/services/Reference/World_Boundaries_and_Places/MapServer/tile/"),
|
||
"Tiles © Esri"
|
||
) {
|
||
override fun getTileURLString(pMapTileIndex: Long): String {
|
||
return baseUrl +
|
||
MapTileIndex.getZoom(pMapTileIndex) + "/" +
|
||
MapTileIndex.getY(pMapTileIndex) + "/" +
|
||
MapTileIndex.getX(pMapTileIndex)
|
||
}
|
||
}
|
||
STADIA_ALIDADE -> XYTileSource(
|
||
"Stadia Alidade Smooth", 1, 20, 256, ".png",
|
||
arrayOf("https://tiles.stadiamaps.com/tiles/alidade_smooth/"),
|
||
"© Stadia Maps, © OpenStreetMap-Mitwirkende"
|
||
)
|
||
STADIA_OUTDOORS -> XYTileSource(
|
||
"Stadia Outdoors", 1, 20, 256, ".png",
|
||
arrayOf("https://tiles.stadiamaps.com/tiles/outdoors/"),
|
||
"© Stadia Maps, © OpenStreetMap-Mitwirkende"
|
||
)
|
||
}
|
||
|
||
companion object {
|
||
val DEFAULT = OSM
|
||
|
||
fun fromKey(key: String?): MapProvider =
|
||
values().firstOrNull { it.key == key } ?: DEFAULT
|
||
}
|
||
}
|
||
|
||
private val Context.mapProviderDataStore by preferencesDataStore(name = "map_provider_settings")
|
||
|
||
/**
|
||
* Persistiert den ausgewählten Karten-Provider app-weit.
|
||
*
|
||
* Hinweis: Die Auswahl ist bewusst app-weit (nicht pro Tour) gehalten – Touren
|
||
* verwalten bisher keine kartenspezifischen Einstellungen, und der Wechsel des
|
||
* Anbieters ist im Karten-Menü jederzeit erreichbar.
|
||
*/
|
||
class MapProviderStore(private val context: Context) {
|
||
|
||
private companion object {
|
||
val KEY_PROVIDER = stringPreferencesKey("selected_map_provider")
|
||
}
|
||
|
||
val provider: Flow<MapProvider> = context.mapProviderDataStore.data.map { prefs ->
|
||
MapProvider.fromKey(prefs[KEY_PROVIDER])
|
||
}
|
||
|
||
suspend fun currentProvider(): MapProvider = provider.first()
|
||
|
||
suspend fun setProvider(provider: MapProvider) {
|
||
context.mapProviderDataStore.edit { prefs ->
|
||
prefs[KEY_PROVIDER] = provider.key
|
||
}
|
||
}
|
||
}
|