メインコンテンツまでスキップ

Restrict map panning

This example demonstrates how to restrict user gestures within a specified bounds using the Mapbox Maps SDK for Android.

Using the example UI, users can select between 4 different options to set the map bounds to:

  • San Francisco
  • Focused around the Equator
  • Around the International Date Line (IDL)
  • Removing the restriction and allowing full view of the globe.

The code below set the different bounds by grabbing the longitude and latitude of two or four different point on the maps and creating CoordinateBounds, and then passing that bounds and the zoom level onto mapboxMap.setBounds which creates the boundary and then is passed into the CameraBoundsOptions.

Android Examples App Available

This example code is part of the Maps SDK for Android Examples App, a working Android project available on GitHub. Android developers are encouraged to run the examples app locally to interact with this example in an emulator and explore other features of the Maps SDK.

See our Run the Maps SDK for Android Examples App tutorial for step-by-step instructions.

RestrictBoundsActivity.kt
package com.mapbox.maps.testapp.examples.camera

import android.annotation.SuppressLint
import android.graphics.Color
import android.os.Bundle
import android.view.Gravity
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatActivity
import com.mapbox.geojson.FeatureCollection
import com.mapbox.geojson.Point
import com.mapbox.geojson.Polygon
import com.mapbox.maps.CameraBoundsOptions
import com.mapbox.maps.CoordinateBounds
import com.mapbox.maps.MapboxMap
import com.mapbox.maps.Style
import com.mapbox.maps.extension.style.layers.generated.FillLayer
import com.mapbox.maps.extension.style.layers.generated.fillLayer
import com.mapbox.maps.extension.style.layers.getLayerAs
import com.mapbox.maps.extension.style.layers.properties.generated.Visibility
import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource
import com.mapbox.maps.extension.style.sources.generated.geoJsonSource
import com.mapbox.maps.extension.style.sources.getSourceAs
import com.mapbox.maps.extension.style.style
import com.mapbox.maps.testapp.R
import com.mapbox.maps.testapp.databinding.ActivityRestrictBoundsBinding

/**
* Test activity showcasing restricting user gestures to a bounds around Iceland, almost worldview and IDL.
*/
class RestrictBoundsActivity : AppCompatActivity() {

private lateinit var mapboxMap: MapboxMap
private lateinit var binding: ActivityRestrictBoundsBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityRestrictBoundsBinding.inflate(layoutInflater)
setContentView(binding.root)
mapboxMap = binding.mapView.mapboxMap
mapboxMap.loadStyle(
style(Style.STANDARD) {
+geoJsonSource(BOUNDS_ID) {
featureCollection(FeatureCollection.fromFeatures(listOf()))
}
+fillLayer(BOUNDS_ID, BOUNDS_ID) {
fillColor(Color.RED)
fillOpacity(0.15)
visibility(Visibility.NONE)
slot("bottom")
}
}
) { setupBounds(SAN_FRANCISCO_BOUND) }
showCrosshair()
}

override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_bounds, menu)
return true
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.menu_action_san_francisco_bounds -> {
setupBounds(SAN_FRANCISCO_BOUND)
true
}
R.id.menu_action_allmost_world_bounds -> {
setupBounds(ALMOST_WORLD_BOUNDS)
true
}
R.id.menu_action_cross_idl -> {
setupBounds(CROSS_IDL_BOUNDS)
true
}
R.id.menu_action_reset -> {
setupBounds(INFINITE_BOUNDS)
true
}
R.id.menu_action_toggle_bounds -> {
toggleShowBounds()
true
}
else -> {
super.onOptionsItemSelected(item)
}
}
}

private fun toggleShowBounds() {
mapboxMap.getStyle {
val boundsFillLayer = it.getLayerAs<FillLayer>(BOUNDS_ID)!!
val visibility: Visibility = boundsFillLayer.visibility!!
when (visibility) {
Visibility.NONE -> boundsFillLayer.visibility(Visibility.VISIBLE)
Visibility.VISIBLE -> boundsFillLayer.visibility(Visibility.NONE)
}
}
}

private fun setupBounds(bounds: CameraBoundsOptions) {
mapboxMap.getStyle { style ->
setupBoundsArea(bounds, style)
}
}

private fun setupBoundsArea(boundsOptions: CameraBoundsOptions, style: Style) {
mapboxMap.setBounds(boundsOptions)
// In this example we always have bounds
val bounds = boundsOptions.bounds!!
if (!bounds.infiniteBounds) {
val northEast = bounds.northeast
val southWest = bounds.southwest
val northWest = Point.fromLngLat(southWest.longitude(), northEast.latitude())
val southEast = Point.fromLngLat(northEast.longitude(), southWest.latitude())
val boundsBox = listOf(northEast, southEast, southWest, northWest, northEast)
// Update the source with the new bounds
style.getSourceAs<GeoJsonSource>(BOUNDS_ID)!!.geometry(
// We only want one polygon: the box around the bounds
Polygon.fromLngLats(listOf(boundsBox))
)
}
}

private fun showCrosshair() {
val crosshair = View(this)
crosshair.layoutParams = FrameLayout.LayoutParams(10, 10, Gravity.CENTER)
crosshair.setBackgroundColor(Color.BLUE)
binding.mapView.addView(crosshair)
}

companion object {
private const val BOUNDS_ID = "BOUNDS_ID"
private val SAN_FRANCISCO_BOUND: CameraBoundsOptions = CameraBoundsOptions.Builder()
.bounds(
CoordinateBounds(
Point.fromLngLat(-122.66336, 37.492987),
Point.fromLngLat(-122.250481, 37.87165),
false
)
)
.minZoom(10.0)
.build()

private val ALMOST_WORLD_BOUNDS: CameraBoundsOptions = CameraBoundsOptions.Builder()
.bounds(
CoordinateBounds(
Point.fromLngLat(-170.0, -20.0),
Point.fromLngLat(170.0, 20.0),
false
)
)
.minZoom(2.0)
.build()

@SuppressLint("Range")
private val CROSS_IDL_BOUNDS: CameraBoundsOptions = CameraBoundsOptions.Builder()
.bounds(
CoordinateBounds(
Point.fromLngLat(170.0202020, -20.0),
Point.fromLngLat(190.0, 20.0),
false
)
)
.minZoom(2.0)
.build()

private val INFINITE_BOUNDS: CameraBoundsOptions = CameraBoundsOptions.Builder()
.bounds(
CoordinateBounds(
Point.fromLngLat(0.0, 0.0),
Point.fromLngLat(0.0, 0.0),
true
)
)
.build()
}
}
この{Type}は役に立ちましたか?