Set the map view to a new viewpoint.

Use case
Navigate programmatically to a specific location on the map, allowing you to zoom in on a particular point.
How to use the sample
The map view has several methods for setting its current viewpoint. Select a viewpoint from the UI to see the viewpoint changed using that method.
How it works
- Create a new
ArcGISMapobject and pass it to theMapViewcomposable’sarcGISMapparameter. - Change the map’s
Viewpointby calling one of the available methods viaMapViewProxy:- Use
MapViewProxy.setViewpointAnimted()to pan to a viewpoint over a specifiedDuration. - Use
MapViewProxy.setViewpointCenter()to center the viewpoint on aPoint. - Use
MapViewProxy.setViewpointGeometry()to set a viewpoint on a givenGeometry
- Use
Relevant API
- ArcGISMap
- Geometry
- MapViewProxy
- Point
- Viewpoint
Additional information
See the various “setViewpoint” methods on MapViewProxy and SceneViewProxy here.
Tags
animate, center, extent, pan, rotate, scale, view, zoom
Sample code
/* Copyright 2026 Esri * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */
package com.esri.arcgismaps.sample.changeviewpoint.components
import android.app.Applicationimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.setValueimport androidx.lifecycle.AndroidViewModelimport androidx.lifecycle.viewModelScopeimport com.arcgismaps.Colorimport com.arcgismaps.geometry.Geometryimport com.arcgismaps.geometry.Pointimport com.arcgismaps.geometry.Polygonimport com.arcgismaps.geometry.SpatialReferenceimport com.arcgismaps.mapping.ArcGISMapimport com.arcgismaps.mapping.BasemapStyleimport com.arcgismaps.mapping.Viewpointimport com.arcgismaps.mapping.symbology.SimpleFillSymbolimport com.arcgismaps.mapping.symbology.SimpleFillSymbolStyleimport com.arcgismaps.mapping.view.Graphicimport com.arcgismaps.mapping.view.GraphicsOverlayimport com.arcgismaps.toolkit.geoviewcompose.MapViewProxyimport com.esri.arcgismaps.sample.changeviewpoint.Rimport com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModelimport kotlinx.coroutines.launchimport kotlin.time.Duration.Companion.seconds
class ChangeViewpointViewModel(app: Application) : AndroidViewModel(app) {
//Create a viewpoint for London, England with a center point and scale private val londonViewpoint = Viewpoint( center = Point( x = 0.1275, y = 51.5072, spatialReference = SpatialReference.wgs84() ), scale = 4e4 )
// Create an ArcGISMap with a basemap style and set the initial viewpoint val arcGISMap = ArcGISMap(BasemapStyle.ArcGISImagery).apply { initialViewpoint = londonViewpoint }
// Create a graphics overlay to display the polygon geometry graphic val graphicsOverlay = GraphicsOverlay()
// Create a MapviewProxy to interact viewpoint changes with the MapView val mapViewProxy = MapViewProxy()
// Track the current visible area for animated viewpoint private var currentVisibleArea: Polygon? by mutableStateOf(null)
// Track the current map scale for animated viewpoint private var currentMapScale: Double? by mutableStateOf(null)
// Create a message dialog view model for handling error messages val messageDialogVM = MessageDialogViewModel()
init { // Create the geometry from JSON and the simple fill symbol for the graphic val griffithParkPolygon = Geometry.fromJsonOrNull( json = app.resources.openRawResource(R.raw.griffith_park_geometry) .bufferedReader() .use { it.readText() } ) as? Polygon
if (griffithParkPolygon != null) { val fillSymbol = SimpleFillSymbol( style = SimpleFillSymbolStyle.Solid, color = Color.fromRgba(r = 0, g = 128, b = 0, a = 179) )
// Create the graphic using the geometry and symbol, and add it to the graphics overlay val griffithParkGraphic = Graphic( geometry = griffithParkPolygon, symbol = fillSymbol ) graphicsOverlay.graphics.add(griffithParkGraphic) } else { messageDialogVM.showMessageDialog("Failed to create geometry from JSON file.") }
viewModelScope.launch { arcGISMap.load().onFailure { messageDialogVM.showMessageDialog(it) } } }
/** * Track the current visible area for animated viewpoint. */ fun onVisibleAreaChanged(newVisibleArea: Polygon) { currentVisibleArea = newVisibleArea }
/** * Track current map scale for animated viewpoint. */ fun onMapScaleChanged(scale: Double) { currentMapScale = scale }
/** * Sets the viewpoint using a bounding geometry from the [graphicsOverlay]. */ fun onGeometrySelected() { val polygon = graphicsOverlay.graphics.firstOrNull()?.geometry ?: return
viewModelScope.launch { mapViewProxy.setViewpointGeometry( boundingGeometry = polygon, paddingInDips = 50.0 ) } }
/** * Sets the viewpoint using center point and scale from the [londonViewpoint]. */ fun onCenterSelected() { val center = londonViewpoint.targetGeometry.extent.center val scale = londonViewpoint.targetScale viewModelScope.launch { mapViewProxy.setViewpointCenter( center = center, scale = scale ) } }
/** * Animates the viewpoint to zoom in and out over duration, using current visible area and map scale */ fun onAnimateSelected() { val center = currentVisibleArea?.extent?.center ?: return val scale = currentMapScale ?: return
viewModelScope.launch { // Zoom in to the current visible area by half the current scale val isAnimationComplete = mapViewProxy.setViewpointAnimated( viewpoint = Viewpoint(center = center, scale = scale / 2), duration = 5.seconds ).getOrElse { messageDialogVM.showMessageDialog(it) false } // Once complete, zoom back out to the original scale if (isAnimationComplete) { mapViewProxy.setViewpointAnimated( viewpoint = Viewpoint(center = center, scale = scale), duration = 5.seconds ) } } }}/* Copyright 2026 Esri * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */
package com.esri.arcgismaps.sample.changeviewpoint
import android.os.Bundleimport androidx.activity.ComponentActivityimport androidx.activity.compose.setContentimport androidx.activity.enableEdgeToEdgeimport androidx.compose.material3.MaterialThemeimport androidx.compose.material3.Surfaceimport androidx.compose.runtime.Composableimport androidx.compose.ui.tooling.preview.Previewimport com.esri.arcgismaps.sample.sampleslib.theme.SampleAppThemeimport com.esri.arcgismaps.sample.changeviewpoint.screens.ChangeViewpointScreenclass MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { SampleAppTheme { ChangeViewpointApp() } } }
@Composable private fun ChangeViewpointApp() { Surface(color = MaterialTheme.colorScheme.background) { ChangeViewpointScreen( sampleName = getString(R.string.change_viewpoint_app_name) ) } }}
@Preview(showBackground = true)@Composablefun ChangeViewpointScreenPreview() { SampleAppTheme { ChangeViewpointScreen( sampleName = "Change Viewpoint" ) }}/* Copyright 2026 Esri * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */
package com.esri.arcgismaps.sample.changeviewpoint.screens
import androidx.compose.foundation.layout.Arrangementimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.Rowimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material3.Buttonimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.Textimport androidx.compose.runtime.Composableimport androidx.compose.ui.Modifierimport androidx.compose.ui.res.stringResourceimport androidx.compose.ui.unit.dpimport androidx.lifecycle.viewmodel.compose.viewModelimport com.arcgismaps.toolkit.geoviewcompose.MapViewimport com.esri.arcgismaps.sample.changeviewpoint.Rimport com.esri.arcgismaps.sample.changeviewpoint.components.ChangeViewpointViewModelimport com.esri.arcgismaps.sample.sampleslib.components.MessageDialogimport com.esri.arcgismaps.sample.sampleslib.components.SampleTopAppBar
/** * Main screen layout for the sample app */@Composablefun ChangeViewpointScreen(sampleName: String) { val mapViewModel: ChangeViewpointViewModel = viewModel()
Scaffold(topBar = { SampleTopAppBar(title = sampleName) }) { paddingValues -> Column( modifier = Modifier .fillMaxSize() .padding(paddingValues), ) { MapView( modifier = Modifier .fillMaxSize() .weight(1f), arcGISMap = mapViewModel.arcGISMap, graphicsOverlays = listOf(mapViewModel.graphicsOverlay), mapViewProxy = mapViewModel.mapViewProxy, onMapScaleChanged = mapViewModel::onMapScaleChanged, onVisibleAreaChanged = mapViewModel::onVisibleAreaChanged )
Row( modifier = Modifier .fillMaxWidth() .padding(all = 8.dp), horizontalArrangement = Arrangement.SpaceEvenly ) { Button(onClick = mapViewModel::onGeometrySelected) { Text(text = stringResource(R.string.geometry)) }
Button(onClick = mapViewModel::onCenterSelected) { Text(text = stringResource(R.string.center_and_scale)) }
Button(onClick = mapViewModel::onAnimateSelected) { Text(text = stringResource(R.string.animate)) } } } mapViewModel.messageDialogVM.apply { if (dialogStatus) { MessageDialog( title = messageTitle, description = messageDescription, onDismissRequest = ::dismissDialog ) } } }}