Skip to content

Restrict Map Panning to a Region

This tutorial shows how to limit the map view to a specific geographic area — useful for city-specific apps, campus maps, or regional applications.

Prerequisites

Restrict Using Camera Bounds

Constrain the map so users cannot pan outside a bounding box:

kotlin
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import org.maplibre.android.camera.CameraPosition
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 RestrictPanActivity : AppCompatActivity() {

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

    // Paris bounding box
    private val parisBounds = LatLngBounds.Builder()
        .include(LatLng(48.9020, 2.4700))  // Northeast
        .include(LatLng(48.8100, 2.2200))  // Southwest
        .build()

    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

            // Set the bounds constraint
            map.setLatLngBoundsForCameraTarget(parisBounds)

            // Also restrict zoom range
            map.setMinZoomPreference(10.0)
            map.setMaxZoomPreference(18.0)

            map.setStyle(
                Style.Builder().fromUri(
                    "https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY"
                )
            )

            // Center on Paris
            map.cameraPosition = CameraPosition.Builder()
                .target(LatLng(48.8566, 2.3522))
                .zoom(12.0)
                .build()
        }
    }

    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)
    }
}

Switchable Region Constraints

Let users switch between predefined regions:

kotlin
import android.widget.Button
import org.maplibre.android.camera.CameraUpdateFactory

private val regions = mapOf(
    "Paris" to Pair(
        LatLngBounds.Builder()
            .include(LatLng(48.9020, 2.4700))
            .include(LatLng(48.8100, 2.2200))
            .build(),
        LatLng(48.8566, 2.3522)
    ),
    "London" to Pair(
        LatLngBounds.Builder()
            .include(LatLng(51.6723, 0.1480))
            .include(LatLng(51.3850, -0.3517))
            .build(),
        LatLng(51.5074, -0.1278)
    ),
    "Berlin" to Pair(
        LatLngBounds.Builder()
            .include(LatLng(52.6755, 13.7611))
            .include(LatLng(52.3383, 13.0884))
            .build(),
        LatLng(52.5200, 13.4050)
    ),
)

private fun setupRegionButtons() {
    findViewById<Button>(R.id.btnParis).setOnClickListener { switchRegion("Paris") }
    findViewById<Button>(R.id.btnLondon).setOnClickListener { switchRegion("London") }
    findViewById<Button>(R.id.btnBerlin).setOnClickListener { switchRegion("Berlin") }
}

private fun switchRegion(name: String) {
    regions[name]?.let { (bounds, center) ->
        // Update bounds constraint
        map.setLatLngBoundsForCameraTarget(bounds)

        // Fly to the new region
        map.animateCamera(
            CameraUpdateFactory.newLatLngZoom(center, 12.0),
            1500
        )
    }
}

Restrict with Zoom Constraints

Combine panning bounds with zoom limits:

kotlin
// Allow only zoom levels 11-16
map.setMinZoomPreference(11.0)
map.setMaxZoomPreference(16.0)

// Restrict panning area
map.setLatLngBoundsForCameraTarget(parisBounds)

// Disable rotation and tilt for a fixed 2D view
map.uiSettings.isRotateGesturesEnabled = false
map.uiSettings.isTiltGesturesEnabled = false

Remove Restrictions

Clear all constraints at runtime:

kotlin
// Remove panning bounds
map.setLatLngBoundsForCameraTarget(null)

// Reset zoom limits
map.setMinZoomPreference(0.0)
map.setMaxZoomPreference(22.0)

// Re-enable all gestures
map.uiSettings.setAllGesturesEnabled(true)

Next Steps


Tip: Set min zoom high enough that the bounds fill the screen — if users can zoom out too far, they'll see the restricted area as a small box on a larger map, which looks odd. Typically minZoom = 10-12 works well for city-level restrictions.