All docschevron-rightMaps SDK for Androidchevron-rightarrow-leftExampleschevron-rightInset map

Inset map

Show a smaller inset map fragment and link it to a larger map for two map interaction.

activity_inset_map
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:mapbox="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mapbox.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
mapbox:mapbox_cameraTargetLat="11.302318"
mapbox:mapbox_cameraTargetLng="106.025839"
mapbox:mapbox_cameraZoom="5.11" />
<androidx.cardview.widget.CardView
android:id="@+id/cardview"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_gravity="bottom|end"
android:layout_marginEnd="16dp"
android:layout_marginBottom="90dp">
<FrameLayout
android:id="@+id/mini_map_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.cardview.widget.CardView>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/show_bounds_toggle_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:src="@drawable/ic_swap_horiz_white_24dp" />
</FrameLayout>
InsetMapActivity.kt
package com.mapbox.maps.testapp.examples
import android.graphics.Color
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.mapbox.geojson.Feature
import com.mapbox.geojson.LineString
import com.mapbox.geojson.Point
import com.mapbox.maps.*
import com.mapbox.maps.extension.observable.eventdata.CameraChangedEventData
import com.mapbox.maps.extension.style.layers.addLayer
import com.mapbox.maps.extension.style.layers.generated.lineLayer
import com.mapbox.maps.extension.style.layers.getLayer
import com.mapbox.maps.extension.style.layers.properties.generated.LineCap
import com.mapbox.maps.extension.style.layers.properties.generated.LineJoin
import com.mapbox.maps.extension.style.layers.properties.generated.Visibility
import com.mapbox.maps.extension.style.sources.addSource
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.getSource
import com.mapbox.maps.plugin.attribution.attribution
import com.mapbox.maps.plugin.compass.compass
import com.mapbox.maps.plugin.delegates.listeners.OnCameraChangeListener
import com.mapbox.maps.plugin.gestures.gestures
import com.mapbox.maps.plugin.logo.logo
import com.mapbox.maps.plugin.scalebar.scalebar
import com.mapbox.maps.testapp.R
import com.mapbox.maps.testapp.databinding.ActivityInsetMapBinding
import com.mapbox.maps.testapp.examples.fragment.MapFragment
/**
* Example demonstrating displaying two maps: main map and small map with lower zoom
* in bottom-right corner with optional bounds showing what area is covered by main map.
*/
class InsetMapActivity : AppCompatActivity(), OnCameraChangeListener {
private lateinit var mainMapboxMap: MapboxMap
private var insetMapboxMap: MapboxMap? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityInsetMapBinding.inflate(layoutInflater)
setContentView(binding.root)
mainMapboxMap = binding.mapView.getMapboxMap()
mainMapboxMap.loadStyleUri(
styleUri = STYLE_URL
) { mainMapboxMap.addOnCameraChangeListener(this@InsetMapActivity) }
var insetMapFragment: MapFragment? =
supportFragmentManager.findFragmentByTag(INSET_FRAGMENT_TAG) as? MapFragment
if (insetMapFragment == null) {
// Create fragment transaction for the inset fragment
val transaction = supportFragmentManager.beginTransaction()
insetMapFragment = MapFragment()
// Add fragmentMap fragment to parent container
transaction.add(R.id.mini_map_fragment_container, insetMapFragment, INSET_FRAGMENT_TAG)
transaction.commit()
}
setInsetMapStyle(insetMapFragment)
binding.showBoundsToggleFab.setOnClickListener {
// Toggle the visibility of the camera bounds LineLayer
insetMapboxMap?.getStyle { style ->
style.getLayer(BOUNDS_LINE_LAYER_LAYER_ID)?.apply {
visibility(if (visibility == Visibility.VISIBLE) Visibility.NONE else Visibility.VISIBLE)
}
}
}
}
private fun setInsetMapStyle(insetMapFragment: MapFragment) {
insetMapFragment.getMapAsync {
insetMapboxMap = it
insetMapboxMap?.loadStyleUri(
styleUri = STYLE_URL
) { style ->
val source = geoJsonSource(BOUNDS_LINE_LAYER_SOURCE_ID) {
feature(Feature.fromGeometry(LineString.fromLngLats(getRectanglePoints())))
}
style.addSource(source)
// The layer properties for our line. This is where we make the line dotted, set the color, etc.
val layer = lineLayer(BOUNDS_LINE_LAYER_LAYER_ID, BOUNDS_LINE_LAYER_SOURCE_ID) {
lineCap(LineCap.ROUND)
lineJoin(LineJoin.ROUND)
lineWidth(3.0)
lineColor(Color.YELLOW)
visibility(Visibility.VISIBLE)
}
style.addLayer(layer)
updateInsetMapLineLayerBounds(style)
}
insetMapFragment.getMapView()?.apply {
logo.enabled = false
scalebar.enabled = false
attribution.enabled = false
compass.enabled = false
gestures.updateSettings {
scrollEnabled = false
pinchToZoomEnabled = false
}
}
}
}
override fun onCameraChanged(eventData: CameraChangedEventData) {
val mainCameraPosition = mainMapboxMap.cameraState
val insetCameraPosition = CameraOptions.Builder()
.zoom(mainCameraPosition.zoom.minus(ZOOM_DISTANCE_BETWEEN_MAIN_AND_INSET_MAPS))
.pitch(mainCameraPosition.pitch)
.bearing(mainCameraPosition.bearing)
.center(mainCameraPosition.center)
.build()
insetMapboxMap?.setCamera(insetCameraPosition)
insetMapboxMap?.getStyle { style -> updateInsetMapLineLayerBounds(style) }
}
private fun updateInsetMapLineLayerBounds(fullyLoadedStyle: Style) {
(fullyLoadedStyle.getSource(BOUNDS_LINE_LAYER_SOURCE_ID) as? GeoJsonSource)?.apply {
feature(Feature.fromGeometry(LineString.fromLngLats(getRectanglePoints())))
}
}
private fun getRectanglePoints(): List<Point> {
val bounds = mainMapboxMap.coordinateBoundsForCamera(
mainMapboxMap.cameraState.toCameraOptions()
)
return listOf(
Point.fromLngLat(bounds.northeast.longitude(), bounds.northeast.latitude()),
Point.fromLngLat(bounds.northeast.longitude(), bounds.southwest.latitude()),
Point.fromLngLat(bounds.southwest.longitude(), bounds.southwest.latitude()),
Point.fromLngLat(bounds.southwest.longitude(), bounds.northeast.latitude()),
Point.fromLngLat(bounds.northeast.longitude(), bounds.northeast.latitude())
)
}
companion object {
private const val STYLE_URL = "mapbox://styles/mapbox/cj5l80zrp29942rmtg0zctjto"
private const val INSET_FRAGMENT_TAG = "com.mapbox.insetMapFragment"
private const val BOUNDS_LINE_LAYER_SOURCE_ID = "BOUNDS_LINE_LAYER_SOURCE_ID"
private const val BOUNDS_LINE_LAYER_LAYER_ID = "BOUNDS_LINE_LAYER_LAYER_ID"
private const val ZOOM_DISTANCE_BETWEEN_MAIN_AND_INSET_MAPS = 3
}
}