Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.what3words.components.compose.maps
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.what3words.components.compose.maps.models.DarkModeStyle

Expand All @@ -11,23 +12,59 @@ enum class MapProvider {
MAPBOX
}

/**
* Object containing default configurations and utility functions for What3Words map components.
*
* This object provides default configurations for various aspects of the What3Words map,
* including map configuration, grid lines, layout, and buttons.
*/
object W3WMapDefaults {

/**
* Data class representing the configuration for the map.
*
* @property darkModeCustomJsonStyle The custom JSON style for dark mode.
* @property gridLineConfig The configuration for grid lines on the map.
*/
data class MapConfig(
val darkModeCustomJsonStyle: String = DarkModeStyle.darkMode,
// Grid view
val gridLineConfig: GridLinesConfig
)

/**
* Data class representing the configuration for grid lines on the map.
*
* @property isGridEnabled Whether the grid is enabled or not.
* @property gridColor The color of the grid lines.
* @property gridLineWidth The width of the grid lines.
* @property zoomSwitchLevel The zoom level at which the grid appearance changes.
* @property gridScale The scale factor for the grid. Determines how much larger the grid is
* compared to the visible camera bounds. A value of 1.0 matches the visible area, while larger
* values (e.g., 2.0) make the grid cover an area twice the size of the visible bounds.
*/
data class GridLinesConfig(
val isGridEnabled: Boolean = true,
val gridColor: Color? = null,
val zoomSwitchLevel: Float = 19f
val isGridEnabled: Boolean,
val gridColor: Color,
val gridLineWidth: Dp,
val zoomSwitchLevel: Float,
val gridScale: Float
)

/**
* Data class representing the layout configuration for the map.
*
* @property contentPadding The padding values for the map content.
*/
data class LayoutConfig(
val contentPadding: PaddingValues
)

/**
* Data class representing the layout configuration for map buttons.
*
* @property contentPadding The padding values for the button layout.
*/
data class ButtonsLayoutConfig(
val contentPadding: PaddingValues
)
Expand All @@ -51,15 +88,19 @@ object W3WMapDefaults {
)
}

fun defaultGridLinesConfig(
private fun defaultGridLinesConfig(
isGridEnabled: Boolean = true,
gridColor: Color? = null,
zoomSwitchLevel: Float = 19f
gridColor: Color = Color.LightGray,
zoomSwitchLevel: Float = 19f,
gridLineWidth: Dp = 1.dp,
gridScale: Float = 6f
): GridLinesConfig {
return GridLinesConfig(
isGridEnabled = isGridEnabled,
gridColor = gridColor,
zoomSwitchLevel = zoomSwitchLevel
zoomSwitchLevel = zoomSwitchLevel,
gridLineWidth = gridLineWidth,
gridScale = gridScale
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ fun W3WGoogleMap(
delay(500)
}
projection?.let {
updateGridBound(projection) { newBound ->
updateGridBound(projection, mapConfig.gridLineConfig) { newBound ->
lastProcessedPosition = position
val newCameraState = W3WGoogleCameraState(cameraPositionState)
newCameraState.gridBound = newBound
Expand Down Expand Up @@ -129,10 +129,12 @@ private fun isSignificantChange(

private suspend fun updateGridBound(
projection: Projection,
gridLinesConfig: W3WMapDefaults.GridLinesConfig,
onGridBoundUpdate: (W3WRectangle) -> Unit
) {
withContext(Dispatchers.IO) {
val lastScaledBounds = scaleBounds(projection.visibleRegion.latLngBounds, projection)
val lastScaledBounds =
scaleBounds(projection.visibleRegion.latLngBounds, projection, gridLinesConfig)
val box = W3WRectangle(
W3WCoordinates(
lastScaledBounds.southwest.latitude,
Expand All @@ -153,8 +155,9 @@ private suspend fun updateGridBound(
private fun scaleBounds(
bounds: LatLngBounds,
projection: Projection,
scale: Float = 6f
gridLinesConfig: W3WMapDefaults.GridLinesConfig
): LatLngBounds {
val scale = gridLinesConfig.gridScale
try {
val center = bounds.center
val centerPoint: Point = projection.toScreenLocation(center)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ package com.what3words.components.compose.maps.providers.googlemap
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap
import com.google.android.gms.maps.model.BitmapDescriptorFactory
Expand Down Expand Up @@ -44,7 +41,7 @@ fun W3WGoogleMapDrawer(
verticalLines = state.gridLines.verticalLines,
horizontalLines = state.gridLines.horizontalLines,
zoomLevel = it.getZoomLevel(),
zoomSwitchLevel = mapConfig.gridLineConfig.zoomSwitchLevel
gridLinesConfig = mapConfig.gridLineConfig
)
}
}
Expand All @@ -53,7 +50,12 @@ fun W3WGoogleMapDrawer(
W3WGoogleMapDrawMarkers(mapConfig.gridLineConfig.zoomSwitchLevel, state.listMakers)

//Draw the selected address
state.selectedAddress?.let { W3WGoogleMapDrawSelectedAddress(mapConfig.gridLineConfig.zoomSwitchLevel, it) }
state.selectedAddress?.let {
W3WGoogleMapDrawSelectedAddress(
mapConfig.gridLineConfig.zoomSwitchLevel,
it
)
}
}


Expand All @@ -62,14 +64,33 @@ fun W3WGoogleMapDrawer(
fun W3WGoogleMapDrawGridLines(
verticalLines: List<W3WCoordinates>,
horizontalLines: List<W3WCoordinates>,
zoomSwitchLevel: Float,
zoomLevel: Float
zoomLevel: Float,
gridLinesConfig: W3WMapDefaults.GridLinesConfig,
) {
W3WGoogleMapGrid(
verticalLines = verticalLines,
horizontalLines = horizontalLines,
zoomLevel = zoomLevel,
zoomSwitchLevel = zoomSwitchLevel
if (zoomLevel < gridLinesConfig.zoomSwitchLevel) {
return
}

val horizontalPolylines = horizontalLines.map { coordinate ->
LatLng(coordinate.lat, coordinate.lng)
}

val verticalPolylines = verticalLines.map { coordinate ->
LatLng(coordinate.lat, coordinate.lng)
}

Polyline(
points = horizontalPolylines,
color = gridLinesConfig.gridColor,
width = gridLinesConfig.gridLineWidth.value,
clickable = false
)

Polyline(
points = verticalPolylines,
color = gridLinesConfig.gridColor,
width = gridLinesConfig.gridLineWidth.value,
clickable = false
)
}

Expand Down Expand Up @@ -129,43 +150,4 @@ fun W3WGoogleMapDrawMarkers(zoomLevel: Float, listMakers: Map<String, List<W3WMa
)
}
}
}

@Composable
@GoogleMapComposable
private fun W3WGoogleMapGrid(
verticalLines: List<W3WCoordinates>,
horizontalLines: List<W3WCoordinates>,
zoomSwitchLevel: Float,
zoomLevel: Float
) {

val horizontalPolylines = horizontalLines.map { coordinate ->
LatLng(coordinate.lat, coordinate.lng)
}

val verticalPolylines = verticalLines.map { coordinate ->
LatLng(coordinate.lat, coordinate.lng)
}

Polyline(
points = horizontalPolylines,
color = Color.LightGray,
width = getGridBorderSizeBasedOnZoomLevel(zoomLevel, zoomSwitchLevel).value,
clickable = false
)

Polyline(
points = verticalPolylines,
color = Color.LightGray,
width = getGridBorderSizeBasedOnZoomLevel(zoomLevel, zoomSwitchLevel).value,
clickable = false
)
}

private fun getGridBorderSizeBasedOnZoomLevel(zoomLevel: Float, zoomSwitchLevel: Float): Dp {
return when {
zoomLevel < zoomSwitchLevel -> 0.dp
else -> 1.dp
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,32 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import com.mapbox.maps.CameraState
import com.mapbox.maps.MapView
import com.mapbox.maps.MapboxMap
import com.mapbox.maps.extension.compose.MapEffect
import com.mapbox.maps.extension.compose.MapboxMap
import com.mapbox.maps.extension.compose.rememberMapState
import com.mapbox.maps.extension.compose.style.GenericStyle
import com.mapbox.maps.plugin.gestures.generated.GesturesSettings
import com.mapbox.maps.plugin.locationcomponent.createDefault2DPuck
import com.mapbox.maps.plugin.locationcomponent.location
import com.mapbox.maps.toCameraOptions
import com.what3words.components.compose.maps.W3WMapDefaults
import com.what3words.components.compose.maps.mapper.toMapBoxMapType
import com.what3words.components.compose.maps.state.W3WMapState
import com.what3words.components.compose.maps.state.camera.W3WCameraState
import com.what3words.components.compose.maps.state.camera.W3WMapboxCameraState
import com.what3words.core.types.geometry.W3WCoordinates
import com.what3words.core.types.geometry.W3WRectangle
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlin.math.abs

/**
* A composable function that displays a What3Words (W3W) map using the Mapbox Maps SDK for Android.
Expand Down Expand Up @@ -50,8 +61,33 @@ fun W3WMapBox(

val mapViewportState = (state.cameraState as W3WMapboxCameraState).cameraState

var lastProcessedCameraState by remember { mutableStateOf(mapViewportState.cameraState) }

LaunchedEffect(mapViewportState) {
onCameraUpdated(W3WMapboxCameraState(mapViewportState))
snapshotFlow { mapViewportState.cameraState }
.conflate()
.filterNotNull()
.onEach { currentCameraState ->
val significantChange =
isSignificantChange(currentCameraState, lastProcessedCameraState)
if (significantChange) {
delay(300)
} else {
delay(500)
}
mapView?.mapboxMap?.let { mapboxMap ->
updateGridBound(
mapboxMap,
mapConfig.gridLineConfig,
onGridBoundUpdate = { newBound ->
lastProcessedCameraState = currentCameraState
val newCameraState = W3WMapboxCameraState(mapViewportState)
newCameraState.gridBound = newBound
onCameraUpdated(newCameraState)
}
)
}
}.launchIn(this)
}


Expand Down Expand Up @@ -104,4 +140,48 @@ fun W3WMapBox(
W3WMapBoxDrawer(state, mapConfig)
content?.invoke()
}
}

private fun isSignificantChange(
newCameraState: CameraState,
lastCameraState: CameraState?
): Boolean {
if (lastCameraState == null) {
return true
}

val latDiff = abs(newCameraState.center.latitude() - lastCameraState.center.latitude())
val lngDiff = abs(newCameraState.center.longitude() - lastCameraState.center.longitude())
val zoomDiff = abs(newCameraState.zoom - lastCameraState.zoom)

return latDiff > 0.01 || lngDiff > 0.01 || zoomDiff > 0.5
}

private fun updateGridBound(
mapboxMap: MapboxMap,
gridLinesConfig: W3WMapDefaults.GridLinesConfig,
onGridBoundUpdate: (W3WRectangle) -> Unit,
) {
val scale = gridLinesConfig.gridScale
val bounds = mapboxMap
.coordinateBoundsForCamera(mapboxMap.cameraState.toCameraOptions())
val center = bounds.center()
val finalNELat =
((scale * (bounds.northeast.latitude() - center.latitude()) + center.latitude()))
val finalNELng =
((scale * (bounds.northeast.longitude() - center.longitude()) + center.longitude()))
val finalSWLat =
((scale * (bounds.southwest.latitude() - center.latitude()) + center.latitude()))
val finalSWLng =
((scale * (bounds.southwest.longitude() - center.longitude()) + center.longitude()))

val box = W3WRectangle(
W3WCoordinates(
finalSWLat,
finalSWLng
),
W3WCoordinates(finalNELat, finalNELng)
)

onGridBoundUpdate.invoke(box)
}
Loading