diff --git a/dhis2_android_maps/src/main/java/org/dhis2/maps/layer/MapLayerManager.kt b/dhis2_android_maps/src/main/java/org/dhis2/maps/layer/MapLayerManager.kt index aed9682ea1b..7f93eb206fd 100644 --- a/dhis2_android_maps/src/main/java/org/dhis2/maps/layer/MapLayerManager.kt +++ b/dhis2_android_maps/src/main/java/org/dhis2/maps/layer/MapLayerManager.kt @@ -28,7 +28,8 @@ class MapLayerManager( var mapLayers: HashMap = hashMapOf() private var mapStyle: MapStyle? = null var styleChangeCallback: ((Style) -> Unit)? = null - var currentStylePosition = 0 + var currentStylePosition = + baseMapManager.baseMapStyles.indexOfFirst { it.isDefault }.takeIf { it > -1 } ?: 0 private val relationShipColors = mutableListOf( @@ -77,6 +78,7 @@ class MapLayerManager( mapStyle?.programDarkColor!!, colorUtils, ) + LayerType.ENROLLMENT_LAYER -> EnrollmentMapLayer( style, featureType ?: FeatureType.POINT, @@ -84,9 +86,11 @@ class MapLayerManager( mapStyle?.programDarkColor!!, colorUtils, ) + LayerType.HEATMAP_LAYER -> HeatmapMapLayer( style, ) + LayerType.RELATIONSHIP_LAYER -> RelationshipMapLayer( style, featureType ?: FeatureType.POINT, @@ -94,12 +98,14 @@ class MapLayerManager( getNextAvailableDrawable(sourceId)?.second, colorUtils, ) + LayerType.EVENT_LAYER -> EventMapLayer( style, featureType ?: FeatureType.POINT, relationShipColors.firstOrNull(), colorUtils, ) + LayerType.TEI_EVENT_LAYER -> TeiEventMapLayer( style, featureType ?: FeatureType.POINT, @@ -107,6 +113,7 @@ class MapLayerManager( mapStyle?.programDarkColor!!, colorUtils, ) + LayerType.FIELD_COORDINATE_LAYER -> FieldMapLayer( style, sourceId!!, diff --git a/dhis2_android_maps/src/main/java/org/dhis2/maps/layer/basemaps/BaseMapHolder.kt b/dhis2_android_maps/src/main/java/org/dhis2/maps/layer/basemaps/BaseMapHolder.kt index a6991bb1a30..95cce7081ae 100644 --- a/dhis2_android_maps/src/main/java/org/dhis2/maps/layer/basemaps/BaseMapHolder.kt +++ b/dhis2_android_maps/src/main/java/org/dhis2/maps/layer/basemaps/BaseMapHolder.kt @@ -1,6 +1,7 @@ package org.dhis2.maps.layer.basemaps import android.graphics.Color +import android.widget.ImageView import androidx.recyclerview.widget.RecyclerView import org.dhis2.maps.R import org.dhis2.maps.databinding.BasemapItemBinding @@ -14,9 +15,11 @@ class BaseMapHolder( itemStyle = bindingAdapterPosition if (baseMap.basemapImage != null) { baseMapImage.setImageDrawable(baseMap.basemapImage) + baseMapImage.scaleType = ImageView.ScaleType.CENTER_CROP } else { baseMapImage.setBackgroundColor(Color.GRAY) baseMapImage.setImageResource(R.drawable.unknown_base_map) + baseMapImage.scaleType = ImageView.ScaleType.FIT_CENTER } basemapName.text = baseMap.basemapName } diff --git a/dhis2_android_maps/src/main/java/org/dhis2/maps/layer/basemaps/BaseMapStyle.kt b/dhis2_android_maps/src/main/java/org/dhis2/maps/layer/basemaps/BaseMapStyle.kt index de829c14e43..3f091cc529c 100644 --- a/dhis2_android_maps/src/main/java/org/dhis2/maps/layer/basemaps/BaseMapStyle.kt +++ b/dhis2_android_maps/src/main/java/org/dhis2/maps/layer/basemaps/BaseMapStyle.kt @@ -3,7 +3,7 @@ package org.dhis2.maps.layer.basemaps import com.google.gson.annotations.SerializedName object BaseMapStyleBuilder { - fun build(id: String, tileUrls: List, attribution: String) = BaseMapStyle( + fun build(id: String, tileUrls: List, attribution: String, isDefault: Boolean) = BaseMapStyle( version = 8, sources = StyleSources( rasterTiles = RasterTiles( @@ -24,18 +24,20 @@ object BaseMapStyleBuilder { ), id = id, glyphs = DEFAULT_GLYPH_URL, + isDefault = isDefault, ) fun internalBaseMap(): BaseMapStyle { return build( - OSM_LIGHT, - listOf( + id = OSM_LIGHT, + tileUrls = listOf( DEFAULT_TILE_URL.replace("{s}", "a"), DEFAULT_TILE_URL.replace("{s}", "b"), DEFAULT_TILE_URL.replace("{s}", "c"), DEFAULT_TILE_URL.replace("{s}", "d"), ), - DEFAULT_ATTRIBUTION, + attribution = DEFAULT_ATTRIBUTION, + isDefault = true, ) } } @@ -46,6 +48,7 @@ data class BaseMapStyle( val layers: List, val id: String, var glyphs: String, + val isDefault: Boolean, ) data class StyleSources( diff --git a/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/MapManager.kt b/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/MapManager.kt index 79a6c9ded6c..5b5bf749d4b 100644 --- a/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/MapManager.kt +++ b/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/MapManager.kt @@ -65,7 +65,10 @@ abstract class MapManager(val mapView: MapView) : LifecycleObserver { val baseMapManager = BaseMapManager(mapView.context, this.mapStyles) setUi() map?.setStyle( - baseMapManager.styleJson(this.mapStyles.first()), + baseMapManager.styleJson( + this.mapStyles.find { it.isDefault } + ?: mapStyles.first(), + ), ) { styleLoaded -> this.style = styleLoaded mapLayerManager = MapLayerManager(mapLoaded, baseMapManager, colorUtils).apply { diff --git a/dhis2_android_maps/src/main/java/org/dhis2/maps/usecases/MapStyleConfiguration.kt b/dhis2_android_maps/src/main/java/org/dhis2/maps/usecases/MapStyleConfiguration.kt index 203611027c7..0eabf21b027 100644 --- a/dhis2_android_maps/src/main/java/org/dhis2/maps/usecases/MapStyleConfiguration.kt +++ b/dhis2_android_maps/src/main/java/org/dhis2/maps/usecases/MapStyleConfiguration.kt @@ -7,6 +7,7 @@ import org.hisp.dhis.android.core.map.layer.MapLayerImageryProvider class MapStyleConfiguration(private val d2: D2) { fun fetchMapStyles(): List { + val defaultMap = d2.settingModule().systemSetting().defaultBaseMap().blockingGet()?.value() return d2.mapsModule().mapLayers().withImageryProviders().blockingGet() .map { mapLayer -> val id = mapLayer.displayName() @@ -16,20 +17,44 @@ class MapStyleConfiguration(private val d2: D2) { mapLayer.subdomains(), ) val attribution = mapLayer.imageryProviders().mapAttribution() - build(id, tileUrls, attribution) + build(id, tileUrls, attribution, defaultMap == mapLayer.uid()) } } } fun String.mapTileUrls(subdomainPlaceholder: String?, subdomains: List?): List { - return subdomains - .takeIf { subdomainPlaceholder != null && !it.isNullOrEmpty() } - ?.map { subdomain -> - this.replace(subdomainPlaceholder!!, subdomain) - } - ?: listOf(this) + return when { + subdomainPlaceholder != null && !subdomains.isNullOrEmpty() -> + subdomains.map { subdomain -> this.replace(subdomainPlaceholder, subdomain) } + + this.contains("{subdomain}") -> + possibleSubdomainsForSubdomain().map { subdomain -> + this.replace("{subdomain}", subdomain) + } + + this.contains("{s}") -> + possibleSubdomainsForS().map { subdomain -> + this.replace("{s}", subdomain) + } + + else -> listOf(this) + } } +private fun possibleSubdomainsForS() = listOf( + "a", + "b", + "c", + "d", +) + +private fun possibleSubdomainsForSubdomain() = listOf( + "t0", + "t1", + "t2", + "t3", +) + fun List?.mapAttribution(): String { if (this == null) return "" val attribution = StringBuilder() diff --git a/dhis2_android_maps/src/test/java/org/dhis2/maps/usecases/MapStyleConfigurationTest.kt b/dhis2_android_maps/src/test/java/org/dhis2/maps/usecases/MapStyleConfigurationTest.kt index 9713a1d11d3..bb6d258e1b1 100644 --- a/dhis2_android_maps/src/test/java/org/dhis2/maps/usecases/MapStyleConfigurationTest.kt +++ b/dhis2_android_maps/src/test/java/org/dhis2/maps/usecases/MapStyleConfigurationTest.kt @@ -52,6 +52,43 @@ class MapStyleConfigurationTest { } } + @Test + fun shouldFetchCustomMaps() { + whenever(d2.mapsModule().mapLayers()) doReturn mock() + whenever(d2.mapsModule().mapLayers().withImageryProviders()) doReturn mock() + whenever(d2.mapsModule().mapLayers().withImageryProviders().blockingGet()) doReturn listOf( + mockMapLayer( + displayName = "basemap 1", + imageUrl = "http://{s}.test.test/x/y/z", + subDomainPlaceHolder = null, + subdomains = null, + imaginaryProviders = listOf( + mockImaginaryProvider("© Maplibre"), + ), + ), + mockMapLayer( + displayName = "basemap 2", + imageUrl = "http://test.test.{subdomain}/x/y/z", + subDomainPlaceHolder = null, + subdomains = null, + imaginaryProviders = listOf( + mockImaginaryProvider("© Maplibre"), + mockImaginaryProvider("© Carto"), + ), + ), + ) + + mapStyleConfiguration.fetchMapStyles().let { result -> + assertTrue(result.size == 2) + assertTrue(result[0].sources.rasterTiles.tiles.size == 4) + assertTrue(result[0].sources.rasterTiles.tiles[0] == "http://a.test.test/x/y/z") + assertTrue(result[0].sources.attribution == "© Maplibre") + assertTrue(result[0].sources.rasterTiles.tiles[1] == "http://b.test.test/x/y/z") + assertTrue(result[1].sources.rasterTiles.tiles.size == 4) + assertTrue(result[1].sources.attribution == "© Maplibre, © Carto") + } + } + private fun mockMapLayer( displayName: String, imageUrl: String, diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 43b5d1265cb..fd3847e9be3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ hilt = '2.47' hiltCompiler = '1.0.0' jacoco = '0.8.10' designSystem = "0.2-20240222.160714-31" -dhis2sdk = "1.10.0-20240207.110936-11" +dhis2sdk = "1.10.0-20240219.122222-17" ruleEngine = "2.1.9" appcompat = "1.6.1" annotation = "1.6.0"