Google Maps Compose library provides cameraPositionState
to accomodate 1 location with zoom using below code
val singapore = LatLng(1.35, 103.87)
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(singapore, 10f)
}
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState)
How to accomplish the same wit multiple locations ? I have a list of locations and I want to initialise GoogleMap
to display all location markers.
LatLngBounds.builder()
to create a minimum bound based on a set of LatLng
points, then use cameraPositionState.move()
to update the camera to the specified boundsInstead of using LatLngs
to specify the camera position, you can use LatLngBounds
instead as arguments to the CameraUpdateFactory.NewLatLngBounds()
documented here: https://developers.google.com/android/reference/com/google/android/gms/maps/CameraUpdateFactory#public-static-cameraupdate-newlatlngbounds-latlngbounds-bounds,-int-padding
But you would need to have the LatLngBounds
value first. In that case, you can use the LatLngBounds.builder()
method to create an instance of a bounds
to be used in your camera update.
So Let's say I have the following coordinates used to instantiate markers:
private val santiago = LatLng(-33.4489, -70.6693)
private val bogota = LatLng(-4.7110, -74.0721)
private val lima = LatLng(-12.0464, -77.0428)
private val salvador = LatLng(-12.9777, -38.5016)
Once I have that, I'll need to instantiate the builder:
val boundsBuilder = LatLngBounds.builder()
Create a list for the coordinates:
val coordinates = listOf(
santiago,
bogota,
lima,
salvador
)
Then loop through it and use the include()
method of the builder()
through each LatLng coordinate:
for (coordinate in coordinates) {
boundsBuilder.include(coordinate)
}
Then build it with the build()
method and store its return value as the bounds
to be used later on in updating your camera.
val bounds = boundsBuilder.build()
Then inside your setContent
, if you want to update the camera on initial Load to include all the markers, you can just use the cameraPositionState.move() method within a LaunchedEffect
composable. And it would look something like this:
LaunchedEffect(key1 = true ){
cameraPositionState.move(
update = CameraUpdateFactory.newLatLngBounds(bounds, 100)
)
}
Notice that the cameraPositionState.move()
method has an update
parameter. That's where you just have to put the bounds
value obtained earlier by making the update
value into CameraUpdateFactory.newLatLngBounds(BOUNDS_VALUE_HERE, PADDING_VALUE_HERE)
Here's a sample implementation that you can try where I also added a button for you to test the camera update outside the initial load:
private val santiago = LatLng(-33.4489, -70.6693)
private val bogota = LatLng(-4.7110, -74.0721)
private val lima = LatLng(-12.0464, -77.0428)
private val salvador = LatLng(-12.9777, -38.5016)
private val center = LatLng(-18.000, -58.000)
private val defaultCameraPosition1 = CameraPosition.fromLatLngZoom(center, 2f)
class StackOverflowSample : ComponentActivity(), OnMapsSdkInitializedCallback {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MapsInitializer.initialize(applicationContext, MapsInitializer.Renderer.LATEST, this)
val boundsBuilder = LatLngBounds.builder()
val coordinates = listOf(
santiago,
bogota,
lima,
salvador
)
for (coordinate in coordinates) {
boundsBuilder.include(coordinate)
}
val bounds = boundsBuilder.build()
setContent {
// Observing and controlling the camera's state can be done with a CameraPositionState
val cameraPositionState = rememberCameraPositionState {
position = defaultCameraPosition1
}
val mapProperties by remember {
mutableStateOf(MapProperties(mapType = MapType.NORMAL))
}
val marker1State = rememberMarkerState(position = santiago)
val marker2State = rememberMarkerState(position = bogota)
val marker3State = rememberMarkerState(position = lima)
val marker4State = rememberMarkerState(position = salvador)
// Drawing on the map is accomplished with a child-based API
val markerClick: (Marker) -> Boolean = {
Log.d(TAG, "${it.title} was clicked")
cameraPositionState.projection?.let { projection ->
Log.d(TAG, "The current projection is: $projection")
}
false
}
Column(
modifier = Modifier.fillMaxSize()
) {
Button(onClick = {
cameraPositionState.move(
update = CameraUpdateFactory.newLatLngBounds(bounds, 100)
)
}) {
Text("Update Camera")
}
Box(Modifier.fillMaxSize()) {
LaunchedEffect(key1 = true ){
cameraPositionState.move(
update = CameraUpdateFactory.newLatLngBounds(bounds, 100)
)
}
GoogleMap(
modifier = Modifier.matchParentSize(),
googleMapOptionsFactory = {
GoogleMapOptions().mapId("DEMO_MAP_ID")
},
cameraPositionState = cameraPositionState,
properties = mapProperties,
onPOIClick = {
Log.d(TAG, "POI clicked: ${it.name}")
}
) {
val textView = TextView(this@AdvancedMarkersActivity)
textView.text = "Hello!!"
textView.setBackgroundColor(Color.BLACK)
textView.setTextColor(Color.YELLOW)
AdvancedMarker(
state = marker4State,
onClick = markerClick,
collisionBehavior = 1,
iconView = textView,
title="Marker 4"
)
val pinConfig = PinConfig.builder()
.setBackgroundColor(Color.MAGENTA)
.setBorderColor(Color.WHITE)
.build()
AdvancedMarker(
state = marker1State,
onClick = markerClick,
collisionBehavior = 1,
pinConfig = pinConfig,
title="Marker 1"
)
val glyphOne = PinConfig.Glyph("A", Color.BLACK)
val pinConfig2 = PinConfig.builder()
.setGlyph(glyphOne)
.build()
AdvancedMarker(
state = marker2State,
onClick = markerClick,
collisionBehavior = 1,
pinConfig = pinConfig2,
title="Marker 2"
)
val glyphImage: Int = ic_menu_myplaces
val descriptor = BitmapDescriptorFactory.fromResource(glyphImage)
val pinConfig3 = PinConfig.builder()
.setGlyph(PinConfig.Glyph(descriptor))
.build()
AdvancedMarker(
state = marker3State,
onClick = markerClick,
collisionBehavior = 1,
pinConfig = pinConfig3,
title="Marker 3"
)
}
}
}
}
}
override fun onMapsSdkInitialized(renderer: MapsInitializer.Renderer) {
when (renderer) {
MapsInitializer.Renderer.LATEST -> Log.d("MapsDemo", "The latest version of the renderer is used.")
MapsInitializer.Renderer.LEGACY -> Log.d("MapsDemo", "The legacy version of the renderer is used.")
else -> {}
}
}
}