Documentación / Vista de mapa / Leer la posición de la cámara

Leer la posición de la cámara

El controlador de MapConductor no tiene getCameraPosition(). Es igual en las 3 plataformas. Como es el primer método que se busca al tocar el mapa, escribo aquí qué usar en su lugar y por qué no se ha proporcionado.

En resumen, la cámara es algo que no se va a buscar, sino que se recibe. El lado del SDK del mapa sabe de los cambios de la cámara y a nosotros solo nos basta con recibirlos.

01 · 3 rutas

Úselas según el caso de uso. En todas llega el mismo MapCameraPosition y el área visible está en visibleRegion.

Quieres el valor actual
mapViewState.cameraPosition — se puede leer en cualquier momento. El área visible es cameraPosition.visibleRegion.bounds.
Quieres seguir los cambios
onCameraMove / onCameraMoveEnd — se dispara también durante el movimiento. Si quieres que la UI siga, usa esto.
Módulo de extensión
Regístralo como controlador de superposición y recibe onCameraChanged. Así no compites por el único slot de escucha del controlador del mapa.
Plataforma
Leer desde el estado
val camera = mapViewState.cameraPosition
val bounds = camera.visibleRegion?.bounds

// Sigue los cambios
MapView(
    state = mapViewState,
    onCameraMoveEnd = { camera -> reload(camera.visibleRegion?.bounds) },
)

02 · Por qué no hay un método get

MapConductor se basa en las 3 plataformas en UI declarativa (Compose / SwiftUI / React). En la UI declarativa, el estado tiene un solo lugar que guarda "cómo está ahora" y la UI solo lo refleja. La cámara también es estado, así que su lugar es el state.

Si se agregan métodos de obtención al controlador, el mismo valor tendrá dos orígenes. Uno es el estado, el otro es la lectura directa del SDK. Ambos deberían ser iguales, pero solo aumenta la posibilidad de desviación. De hecho, sucedió así; antes de eliminarlo, estábamos armando la cámara dos veces en el mismo marco: una vez para el evento del SDK y la otra era la recreación en cada redibujado.

Un flujo de tipo push
Evento de cámara del SDK de mapas
        ↓  el proveedor lee los valores crudos del SDK, los pasa al zoom unificado y adjunta visibleRegion
   state.updateCameraPosition(camera)
        ↓
   ├─ mapViewState.cameraPosition                    (legible en cualquier momento)
   ├─ onCameraMove / onCameraMoveEnd                 (callbacks de la app)
   └─ onCameraChanged de las capas registradas       (módulos de extensión)
Esta forma es la misma en android-sdk / ios-sdk / react-sdk. Los dos SDK nativos no tenían originariamente una API de obtención en el controlador; solo react-sdk tenía una, así que la eliminamos el 2026-08-06.

03 · El costo de pull

Si se habla solo de diseño, se tiende a decir “podrías agregar uno más”, así que dejo aquí mediciones reales. Armar la cámara una vez no es barato.

Lo que se hace en una sola llamada

Leer center / zoom / bearing / tilt del SDK → convertir a un zoom unificado con ZoomAltitudeConverter específico del proveedor → retroyectar las cuatro esquinas de la pantalla y construir visibleRegion. El último es el más costoso y no es un simple getter.

Valores medidos. Abrí la página de marcadores de MapLibre en Chromium y conté cuántas veces se llamaba a getCameraPosition() en un solo arrastre (alrededor de 20 fotogramas de desplazamiento).

Carga inicial
Un arrastre
Cuando existían métodos de obtención
5
86
Después de eliminar
3
30

Se redujo aproximadamente un 65 %. Lo reducido es todo recreación. Aunque en el evento del SDK se armaba una vez y se enviaba al estado, en cada redibujado la vista volvía a armar el mismo valor. El llamador estaba en las 13 vistas, lo que significa que todas descartaban el valor que acababan de empujar poco antes.

Por cierto, en estado de reposo es 0 veces. No se estaba desbocando; era un tipo de costo que afecta con seguridad solo mientras se opera. Como se añade en el momento más pesado del mapa, es algo que se nota fácilmente.

Implementación mostrada en video · Android + MapLibreKotlin · Jetpack Compose
var camera by remember { mutableStateOf<MapCameraPosition?>(null) }

MapLibreMapView(
    state = mapViewState,
    modifier = Modifier.weight(1f),
    // Fires throughout the gesture — keep this handler cheap
    onCameraMove = { position -> camera = position },
    // Once, when the map settles. Fetching and redrawing belong here
    onCameraMoveEnd = { position -> viewModel.onCameraSettled(position) },
)

camera?.let { position ->
    Text("lat  %.5f".format(position.position.latitude))
    Text("lng  %.5f".format(position.position.longitude))
    Text("zoom %.2f".format(position.zoom))
    // Read the visible region off the value you were handed; do not rebuild it
    position.visibleRegion?.bounds?.let { bounds ->
        Text("SW ${bounds.southWest}  NE ${bounds.northEast}")
    }
}
Video de ejemplo · Leer valores mientras se mueve el mapa
Video aún no grabadoMientras se arrastra, se sigue actualizando en pantalla la latitud, longitud y zoom recibidos por devolución de llamada. La diferencia entre pull y push se manifiesta en esta forma de seguimiento.

04 · Un retraso de un fotograma

mapViewState.cameraPosition es “el último valor enviado por push”, así que teóricamente puede estar un fotograma más viejo que el valor actual del SDK. Este es el único motivo sustancial para agregar un método de obtención, así que lo escribo con honestidad.

En la práctica

onCameraMove se activa en cada frame incluso durante el movimiento, por lo que state siempre está al día con el frame inmediatamente anterior. No hay diferencias perceptibles en la visualización, obtención de datos ni evaluación de rangos. De hecho, tras eliminar los métodos de obtención, las 41 pruebas de navegador siguen pasando sin cambios.

Si llega a marcar la diferencia

Renderizado completamente sincronizado con los frames durante el arrastre. Sin embargo, dado que esto depende del ciclo de renderizado del SDK del mapa, es un ámbito donde se debe usar la vía de escape descrita más adelante, no la API unificada.

05 · Aun así, cuando se necesitan los valores brutos

Si a través de statе no es suficiente — por ejemplo, si quieres sincronizarte con el bucle de renderizado del SDK del mapa —, obtén la instancia nativa del mapa mediante MapViewHolder sin desviarte por la API unificada. Es código específico del proveedor, pero a cambio es lo más rápido y lo más preciso.

Descender al nivel nativo
const holder = mapViewState.getMapViewHolder();
const map = holder?.map as maplibregl.Map | undefined;
const liveZoom = map?.getZoom();
Dentro de cada proveedor existe una lógica que convierte los valores brutos del SDK a un zoom unificado; los valores para los eventos de cámara se crean allí. Sin embargo, esto es interno de la implementación y no se expone en la interfaz pública. Las tres plataformas siguen el mismo enfoque (android-sdk tiene getMapCameraPosition() privado, ios-sdk tiene currentCameraPosition(from:) privado).

Páginas relacionadas