Skip to content

Fit Map to a Bounding Box

This tutorial shows how to automatically zoom and position your MapMetrics Android map to fit a set of points, markers, or a region.

Prerequisites

Fit to Multiple Markers

Automatically adjust the camera to show all markers:

kotlin
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import org.maplibre.android.annotations.MarkerOptions
import org.maplibre.android.camera.CameraUpdateFactory
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.geometry.LatLngBounds
import org.maplibre.android.maps.MapView
import org.maplibre.android.maps.MapMetricsMap
import org.maplibre.android.maps.Style

class FitBoundsActivity : AppCompatActivity() {

    private lateinit var mapView: MapView
    private lateinit var map: MapMetricsMap

    private val cities = listOf(
        Pair(LatLng(48.8566, 2.3522), "Paris"),
        Pair(LatLng(51.5074, -0.1278), "London"),
        Pair(LatLng(52.5200, 13.4050), "Berlin"),
        Pair(LatLng(41.9028, 12.4964), "Rome"),
        Pair(LatLng(40.4168, -3.7038), "Madrid"),
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_map)

        mapView = findViewById(R.id.mapView)
        mapView.onCreate(savedInstanceState)
        mapView.getMapAsync { mapMetricsMap ->
            map = mapMetricsMap

            map.setStyle(
                Style.Builder().fromUri(
                    "https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY"
                )
            ) { style ->
                addMarkersAndFit()
            }
        }
    }

    private fun addMarkersAndFit() {
        // Add markers
        val boundsBuilder = LatLngBounds.Builder()

        for ((position, name) in cities) {
            map.addMarker(
                MarkerOptions()
                    .position(position)
                    .title(name)
            )
            boundsBuilder.include(position)
        }

        // Fit camera to show all markers with padding
        map.easeCamera(
            CameraUpdateFactory.newLatLngBounds(
                boundsBuilder.build(),
                100 // padding in pixels
            ),
            1000 // animation duration
        )
    }

    override fun onStart() { super.onStart(); mapView.onStart() }
    override fun onResume() { super.onResume(); mapView.onResume() }
    override fun onPause() { super.onPause(); mapView.onPause() }
    override fun onStop() { super.onStop(); mapView.onStop() }
    override fun onDestroy() { super.onDestroy(); mapView.onDestroy() }
    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        mapView.onSaveInstanceState(outState)
    }
}

Fit to a Predefined Region

Zoom to specific geographic regions with buttons:

kotlin
import android.widget.Button

private val regions = mapOf(
    "Europe" to LatLngBounds.Builder()
        .include(LatLng(71.0, -25.0))   // Northwest
        .include(LatLng(35.0, 45.0))    // Southeast
        .build(),
    "North America" to LatLngBounds.Builder()
        .include(LatLng(72.0, -170.0))
        .include(LatLng(15.0, -50.0))
        .build(),
    "Asia" to LatLngBounds.Builder()
        .include(LatLng(55.0, 25.0))
        .include(LatLng(-10.0, 150.0))
        .build(),
)

private fun setupRegionButtons() {
    findViewById<Button>(R.id.btnEurope).setOnClickListener {
        fitToRegion("Europe")
    }
    findViewById<Button>(R.id.btnNorthAmerica).setOnClickListener {
        fitToRegion("North America")
    }
    findViewById<Button>(R.id.btnAsia).setOnClickListener {
        fitToRegion("Asia")
    }
}

private fun fitToRegion(name: String) {
    regions[name]?.let { bounds ->
        map.animateCamera(
            CameraUpdateFactory.newLatLngBounds(bounds, 50),
            2000
        )
    }
}

Fit Bounds with Custom Padding

Use different padding per edge for UI elements:

kotlin
// Different padding for each side: [left, top, right, bottom]
val paddingPixels = intArrayOf(50, 200, 50, 100) // extra top for header

val cameraPosition = map.getCameraForLatLngBounds(
    boundsBuilder.build(),
    paddingPixels
)

cameraPosition?.let {
    map.animateCamera(
        CameraUpdateFactory.newCameraPosition(it),
        1500
    )
}

Fit to Route Points

Zoom to fit a polyline route:

kotlin
import org.maplibre.geojson.LineString
import org.maplibre.geojson.Point

private fun fitToRoute() {
    val routePoints = listOf(
        LatLng(48.8566, 2.3522),   // Paris
        LatLng(50.8503, 4.3517),   // Brussels
        LatLng(52.3676, 4.9041),   // Amsterdam
        LatLng(53.5511, 9.9937),   // Hamburg
        LatLng(52.5200, 13.4050),  // Berlin
    )

    val bounds = LatLngBounds.Builder()
    for (point in routePoints) {
        bounds.include(point)
    }

    map.animateCamera(
        CameraUpdateFactory.newLatLngBounds(bounds.build(), 80),
        2000
    )
}

Fit Bounds Options

ParameterTypeDescription
boundsLatLngBoundsSouthwest + northeast corners to fit
paddingIntEqual padding on all sides (pixels)
paddingIntArrayPer-side padding: [left, top, right, bottom]
durationIntAnimation duration in milliseconds

Animation Methods for Bounds

MethodBehavior
moveCamera()Instant jump to bounds
easeCamera()Constant-speed animation
animateCamera()Natural ease-in/ease-out

Next Steps


Tip: Always add padding (50-100px minimum) when fitting bounds so markers at the edge aren't clipped by the screen boundary. For maps with floating UI panels, use per-side padding to account for the panel area.