diff --git a/lib-compose/src/main/java/com/what3words/components/compose/maps/W3WMapManager.kt b/lib-compose/src/main/java/com/what3words/components/compose/maps/W3WMapManager.kt index 4b0ddaec..092b6c3f 100644 --- a/lib-compose/src/main/java/com/what3words/components/compose/maps/W3WMapManager.kt +++ b/lib-compose/src/main/java/com/what3words/components/compose/maps/W3WMapManager.kt @@ -9,6 +9,7 @@ import com.mapbox.geojson.Point import com.mapbox.maps.CameraState import com.mapbox.maps.EdgeInsets import com.mapbox.maps.extension.compose.animation.viewport.MapViewportState +import com.what3words.androidwrapper.datasource.text.api.error.BadBoundingBoxError import com.what3words.androidwrapper.datasource.text.api.error.BadBoundingBoxTooBigError import com.what3words.components.compose.maps.W3WMapDefaults.LOCATION_DEFAULT import com.what3words.components.compose.maps.W3WMapDefaults.MAKER_COLOR_DEFAULT @@ -34,6 +35,8 @@ import com.what3words.core.types.common.W3WResult import com.what3words.core.types.domain.W3WAddress import com.what3words.core.types.geometry.W3WCoordinates import com.what3words.core.types.language.W3WRFC5646Language +import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -243,7 +246,7 @@ class W3WMapManager( _mapState.value = mapState.value.addOrUpdateMarker( marker = W3WMarker( words = c23wa.value.words, - square = c23wa.value.square?.toW3WSquare(), + square = c23wa.value.square!!.toW3WSquare(), latLng = c23wa.value.center?.toW3WLatLong() ?: LOCATION_DEFAULT, color = markerColor ) @@ -282,7 +285,7 @@ class W3WMapManager( _mapState.value = mapState.value.addOrUpdateMarker( marker = W3WMarker( words = c23wa.value.words, - square = c23wa.value.square?.toW3WSquare(), + square = c23wa.value.square!!.toW3WSquare(), latLng = c23wa.value.center?.toW3WLatLong() ?: LOCATION_DEFAULT, color = markerColor ) @@ -310,7 +313,7 @@ class W3WMapManager( _mapState.value = mapState.value.copy( selectedAddress = W3WMarker( words = c23wa.value.words, - square = c23wa.value.square?.toW3WSquare(), + square = c23wa.value.square!!.toW3WSquare(), latLng = c23wa.value.center?.toW3WLatLong() ?: LOCATION_DEFAULT, color = MAKER_COLOR_DEFAULT ) @@ -325,12 +328,24 @@ class W3WMapManager( } } + // Method used to test add/remove drawn markers on map. To be removed + fun removeMarkerAtWords(words: String) { + _mapState.update { currentState -> + val updatedListMakers = currentState.listMakers.mapValues { (_, listMarker) -> + listMarker.copy(markers = listMarker.markers.filter { it.words != words } + .toImmutableList()) + }.filter { (_, listMarker) -> listMarker.markers.isNotEmpty() }.toImmutableMap() + + currentState.copy(listMakers = updatedListMakers) + } + } + private suspend fun calculateGridPolylines(cameraState: W3WCameraState<*>): W3WGridLines { return withContext(Dispatchers.IO) { cameraState.gridBound?.let { safeBox -> when (val grid = textDataSource.gridSection(safeBox)) { is W3WResult.Failure -> { - if (grid.error is BadBoundingBoxTooBigError) { + if (grid.error is BadBoundingBoxTooBigError || grid.error is BadBoundingBoxError) { return@withContext W3WGridLines() } else { throw grid.error diff --git a/lib-compose/src/main/java/com/what3words/components/compose/maps/models/W3WMarker.kt b/lib-compose/src/main/java/com/what3words/components/compose/maps/models/W3WMarker.kt index ec9d3ece..3a9bd6d5 100644 --- a/lib-compose/src/main/java/com/what3words/components/compose/maps/models/W3WMarker.kt +++ b/lib-compose/src/main/java/com/what3words/components/compose/maps/models/W3WMarker.kt @@ -5,8 +5,8 @@ import androidx.compose.ui.graphics.Color data class W3WMarker( val words: String, val latLng: W3WLatLng, - val square: W3WSquare? = null, - val color: W3WMarkerColor? = null, + val square: W3WSquare, + val color: W3WMarkerColor, val title: String? = null, val snippet: String? = null ) diff --git a/lib-compose/src/main/java/com/what3words/components/compose/maps/providers/mapbox/W3WMapBoxDrawers.kt b/lib-compose/src/main/java/com/what3words/components/compose/maps/providers/mapbox/W3WMapBoxDrawers.kt index 9427419e..f2c4d047 100644 --- a/lib-compose/src/main/java/com/what3words/components/compose/maps/providers/mapbox/W3WMapBoxDrawers.kt +++ b/lib-compose/src/main/java/com/what3words/components/compose/maps/providers/mapbox/W3WMapBoxDrawers.kt @@ -1,24 +1,36 @@ package com.what3words.components.compose.maps.providers.mapbox import android.content.Context +import android.graphics.Bitmap import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Dp import com.mapbox.geojson.Point +import com.mapbox.maps.extension.compose.MapEffect import com.mapbox.maps.extension.compose.MapboxMapComposable import com.mapbox.maps.extension.compose.annotation.generated.PointAnnotation import com.mapbox.maps.extension.compose.annotation.generated.PolylineAnnotation import com.mapbox.maps.extension.compose.annotation.rememberIconImage +import com.mapbox.maps.extension.compose.style.PointListValue +import com.mapbox.maps.extension.compose.style.layers.generated.RasterLayer +import com.mapbox.maps.extension.compose.style.sources.generated.rememberImageSourceState +import com.mapbox.maps.extension.style.sources.generated.ImageSource +import com.mapbox.maps.extension.style.sources.getSourceAs +import com.mapbox.maps.extension.style.sources.updateImage import com.what3words.components.compose.maps.W3WMapDefaults import com.what3words.components.compose.maps.models.W3WLatLng import com.what3words.components.compose.maps.models.W3WMarker import com.what3words.components.compose.maps.state.W3WListMarker import com.what3words.components.compose.maps.state.W3WMapState +import com.what3words.components.compose.maps.utils.getFillGridMarkerBitmap import com.what3words.components.compose.maps.utils.getMarkerBitmap +import com.what3words.components.compose.maps.utils.getPinBitmap import com.what3words.map.components.compose.R @@ -44,6 +56,14 @@ fun W3WMapBoxDrawer(state: W3WMapState, mapConfig: W3WMapDefaults.MapConfig) { selectedMarker = state.selectedAddress ) } + + if (state.listMakers.isNotEmpty()) { + W3WMapBoxDrawSavedAddress( + cameraState.getZoomLevel(), + mapConfig.gridLineConfig.zoomSwitchLevel, + state.listMakers + ) + } } } @@ -90,10 +110,88 @@ fun W3WMapBoxDrawSelectedAddress( @Composable @MapboxMapComposable -fun W3WMapBoxDrawMarkers(zoomLevel: Float, listMakers: Map) { - //TODO: Draw select for zoom in: filled square +fun W3WMapBoxDrawSavedAddress( + zoomLevel: Float, + zoomSwitchLevel: Float, + listMakers: Map +) { + if (zoomLevel < zoomSwitchLevel) { + DrawZoomOutSavedMarkers(listMakers) + } else { + DrawZoomInSavedMarkers(listMakers) + } +} + +@Composable +@MapboxMapComposable +fun DrawZoomOutSavedMarkers(listMakers: Map) { + val context = LocalContext.current + val density = LocalDensity.current.density + + listMakers.forEach { + it.value.markers.forEach { marker -> + val icon = rememberIconImage( + key = marker.words, + painter = BitmapPainter( + getPinBitmap(context, density, marker.color!!).asImageBitmap() + ) + ) + + PointAnnotation( + point = Point.fromLngLat( + marker.latLng.lng, + marker.latLng.lat + ) + ) { + iconImage = icon + } + } + } +} + +@Composable +@MapboxMapComposable +fun DrawZoomInSavedMarkers(listMakers: Map) { + val context = LocalContext.current + val density = LocalDensity.current.density + + listMakers.forEach { markers -> + markers.value.markers.forEach { marker -> + val id = String.format( + ID_SAVED_ADDRESS_IMAGE_SOURCE, + marker.words + ) + + val square = marker.square + + val bitmap = getFillGridMarkerBitmap( + context, + density, + marker.color + ) + + MapEffect(Unit) { + val imageSource: ImageSource = it.mapboxMap.getSourceAs(id)!! + imageSource.updateImage(bitmap) + } + + RasterLayer( + sourceState = rememberImageSourceState(sourceId = id) { + coordinates = PointListValue( + Point.fromLngLat(square.southwest.lng, square.northeast.lat), + Point.fromLngLat(square.northeast.lng, square.northeast.lat), + Point.fromLngLat(square.northeast.lng, square.southwest.lat), + Point.fromLngLat(square.southwest.lng, square.southwest.lat), + ) + } + ) + } + } +} - //TODO: Draw select for zoom out: circle +@Composable +private fun rememberImageBitmap(bitmap: Bitmap): ImageBitmap { + return remember(bitmap) { bitmap.asImageBitmap() } } @Composable @@ -162,4 +260,6 @@ private fun getGridSelectedBorderSizeBasedOnZoomLevel( else -> context.resources.getDimension(R.dimen.grid_selected_width_mapbox_2px).toDouble() } -} \ No newline at end of file +} + +private const val ID_SAVED_ADDRESS_IMAGE_SOURCE = "image_source-saved-address-%s" \ No newline at end of file diff --git a/lib-compose/src/main/java/com/what3words/components/compose/maps/state/camera/W3WGoogleCameraState.kt b/lib-compose/src/main/java/com/what3words/components/compose/maps/state/camera/W3WGoogleCameraState.kt index 777c73f7..6d8ccfbe 100644 --- a/lib-compose/src/main/java/com/what3words/components/compose/maps/state/camera/W3WGoogleCameraState.kt +++ b/lib-compose/src/main/java/com/what3words/components/compose/maps/state/camera/W3WGoogleCameraState.kt @@ -14,8 +14,8 @@ import kotlinx.coroutines.launch class W3WGoogleCameraState(override val cameraState: CameraPositionState) : W3WCameraState { - companion object{ - const val MY_LOCATION_ZOOM = 21f + companion object { + const val MY_LOCATION_ZOOM = 19f } override var gridBound: W3WRectangle? = null @@ -41,9 +41,9 @@ class W3WGoogleCameraState(override val cameraState: CameraPositionState) : updateCameraPosition( CameraPosition( LatLng(coordinates.lat, coordinates.lng), - zoom?:cameraState.position.zoom, - bearing?:cameraState.position.tilt, - tilt?:cameraState.position.bearing + zoom ?: cameraState.position.zoom, + bearing ?: cameraState.position.tilt, + tilt ?: cameraState.position.bearing ), animate ) } diff --git a/lib-compose/src/main/java/com/what3words/components/compose/maps/state/camera/W3WMapboxCameraState.kt b/lib-compose/src/main/java/com/what3words/components/compose/maps/state/camera/W3WMapboxCameraState.kt index c96f3bd1..75e6bc32 100644 --- a/lib-compose/src/main/java/com/what3words/components/compose/maps/state/camera/W3WMapboxCameraState.kt +++ b/lib-compose/src/main/java/com/what3words/components/compose/maps/state/camera/W3WMapboxCameraState.kt @@ -11,7 +11,7 @@ class W3WMapboxCameraState(override val cameraState: MapViewportState) : W3WCameraState { companion object{ - const val MY_LOCATION_ZOOM = MAX_ZOOM + const val MY_LOCATION_ZOOM = 19.0 } override var gridBound: W3WRectangle? = null diff --git a/lib-compose/src/main/java/com/what3words/components/compose/maps/utils/BitmapUtils.kt b/lib-compose/src/main/java/com/what3words/components/compose/maps/utils/BitmapUtils.kt index 115979fd..3909cf52 100644 --- a/lib-compose/src/main/java/com/what3words/components/compose/maps/utils/BitmapUtils.kt +++ b/lib-compose/src/main/java/com/what3words/components/compose/maps/utils/BitmapUtils.kt @@ -41,6 +41,33 @@ fun getMarkerBitmap( ) } +fun getFillGridMarkerBitmap( + context: Context, + scale: Float, + colorMarker: W3WMarkerColor, + size: Int = 32, +): Bitmap { + + return getBitMapFromPathData( + listOf( + DrawPath(context.getString(R.string.path_fill_grid_marker_background), + Paint().apply { + color = colorMarker.background.toArgb() + } + ), + DrawPath(context.getString(R.string.path_fill_grid_marker_slashes), + Paint().apply { + color = colorMarker.slash.toArgb() + } + ) + ), + size, + size, + scale + ) + +} + fun getPinBitmap( context: Context, scale: Float, diff --git a/lib-compose/src/main/res/values/path_data.xml b/lib-compose/src/main/res/values/path_data.xml index 8a75b5ee..aaff1427 100644 --- a/lib-compose/src/main/res/values/path_data.xml +++ b/lib-compose/src/main/res/values/path_data.xml @@ -4,4 +4,6 @@ M19.875,14.787C20.592,15.048 20.969,15.821 20.717,16.513L15.656,30.42C15.404,31.112 14.618,31.462 13.901,31.201C13.184,30.94 12.806,30.167 13.059,29.475L18.12,15.568C18.372,14.875 19.158,14.526 19.875,14.787ZM26.622,14.799C27.339,15.06 27.716,15.833 27.464,16.525L22.403,30.432C22.15,31.124 21.365,31.474 20.648,31.213C19.93,30.952 19.553,30.179 19.805,29.487L24.867,15.58C25.119,14.887 25.905,14.538 26.622,14.799ZM34.243,16.525C34.495,15.832 34.118,15.059 33.401,14.798C32.683,14.537 31.898,14.887 31.646,15.579L26.584,29.486C26.332,30.179 26.709,30.952 27.426,31.213C28.143,31.474 28.929,31.124 29.181,30.431L34.243,16.525Z M12,1L12,1A11,11 0,0 1,23 12L23,12A11,11 0,0 1,12 23L12,23A11,11 0,0 1,1 12L1,12A11,11 0,0 1,12 1z M9.938,7.893C10.296,8.023 10.485,8.41 10.359,8.756L7.828,15.71C7.702,16.056 7.309,16.231 6.95,16.1C6.592,15.97 6.403,15.583 6.529,15.237L9.06,8.283C9.186,7.937 9.579,7.762 9.938,7.893ZM13.311,7.899C13.67,8.03 13.858,8.416 13.732,8.762L11.201,15.716C11.075,16.062 10.683,16.237 10.324,16.106C9.965,15.976 9.777,15.589 9.903,15.243L12.434,8.29C12.56,7.943 12.953,7.769 13.311,7.899ZM17.122,8.762C17.247,8.416 17.059,8.029 16.7,7.899C16.342,7.768 15.949,7.943 15.823,8.289L13.292,15.243C13.166,15.589 13.355,15.975 13.713,16.106C14.072,16.236 14.465,16.061 14.591,15.715L17.122,8.762Z + M0,0h32v32h-32z + M13.1694,9.8419C13.7071,10.0377 13.9898,10.6172 13.8008,11.1364L10.0058,21.5631C9.8169,22.0823 9.2277,22.3446 8.69,22.1488C8.1523,21.9531 7.8696,21.3736 8.0586,20.8544L11.8536,10.4277C12.0425,9.9085 12.6316,9.6462 13.1694,9.8419ZM18.2279,9.8513C18.7657,10.047 19.0484,10.6266 18.8594,11.1458L15.0644,21.5725C14.8754,22.0917 14.2863,22.3539 13.7486,22.1582C13.2109,21.9625 12.9282,21.3829 13.1171,20.8637L16.9121,10.4371C17.1011,9.9178 17.6902,9.6556 18.2279,9.8513ZM23.9413,11.1449C24.1303,10.6257 23.8475,10.0461 23.3098,9.8504C22.7721,9.6547 22.183,9.917 21.994,10.4362L18.199,20.8628C18.01,21.382 18.2928,21.9616 18.8305,22.1573C19.3682,22.353 19.9573,22.0908 20.1463,21.5716L23.9413,11.1449Z \ No newline at end of file