Documentación / Vista de mapa / Cámara

Cámara: la parte más difícil de la abstracción

En MapConductor, la unificación de la cámara es lo más difícil y valioso. Un modelo de cámara compuesto por position, zoom, bearing, tilt y visibleRegion se ha estandarizado con el mismo significado en más de 10 motores de mapas. Si se pasa la misma MapCameraPosition, se obtiene la misma vista con cualquier proveedor.

POSITION
lat / lng
ZOOM
0 – 22
BEARING
0 – 360°
TILT
−60 – 60°

01 · Por qué tiene sentido unificar

Cada motor de mapas tiene su propio modelo de cámara. Algunos hablan de zoom, otros de altitud de la cámara, unos giran alrededor de un punto de interés en el suelo, otros mueven el punto de vista en sí. Si se usa el SDK nativo tal cual, al cambiar de proveedor se obtiene instantáneamente "mismo número pero vista diferente", lo que obliga a reescribir la lógica de la pantalla.

MapConductor encapsula esta diferencia en una capa de adaptador y expone a la aplicación un único modelo de cámara. Como resultado, la posición de la cámara se convierte en un valor que se puede transportar entre proveedores. En la página Camera Sync de la aplicación de ejemplo, se puede comprobar cómo una única posición de cámara se envía a todos los proveedores y se alinean en el mismo lugar, la misma escala y la misma inclinación.

DIFFERENCE 01

¿Zoom o altitud?

Los sistemas de mosaicos 2D expresan la escala mediante el nivel de zoom, mientras que los sistemas 3D lo hacen mediante la altitud de la cámara o la distancia de visión. Las unidades y las referencias también difieren.

DIFFERENCE 02

¿Punto de enfoque o punto de vista?

Conviven un modelo que gira alrededor de un punto en la superficie (órbita) y un modelo que se basa en la posición y orientación de la propia cámara.

DIFFERENCE 03

Rango de inclinación permitido

El ángulo máximo de inclinación y si se puede inclinar hacia arriba varían según el motor. Ninguno tiene una inclinación negativa.

Figura · cómo un solo moveCameraTo llega a la misma vista en cada engine
val camera = MapCameraPosition(
    position = geoPoint,
    zoom = 18.0,
    tilt = 70.0,
)
moveCameraTo(camera)
MapConductor
Modelo de cámara unificado
position / zoom · bearing / tilt
Por proveedor
adaptador de SDK de mapas
Por proveedor
adaptador de SDK de mapas
Los adaptadores traducen a la representación de cámara nativa de cada uno. Aquí se absorben las diferencias entre basado en zoom, basado en altitud y sin inclinación hacia arriba.
Engines basadas en zoom (Google Maps · Mapbox · MapLibre …)
Engines basadas en altitud (ArcGIS · Cesium · MapKit …)
Un solo punto de entrada; distintas expresiones nativas de salida, pero el mismo lugar, la misma escala y la misma inclinación en pantalla.
Video de ejemplo · Camera Sync (sincronizar una posición de cámara con todos los proveedores)

02 · Modelo de cámara unificado

La cámara se representa con un único MapCameraPosition. No hay tipos por proveedor; simplemente se crea este valor y se pasa a moveCameraTo.

Propiedades
Descripción
position
La coordenada central en el suelo a la que apunta la cámara.
zoom
Escala. El nivel de zoom de Google Maps se normaliza para todos los proveedores.
bearing
Orientación (ángulo de rotación desde el norte verdadero).
tilt
Inclinación. 0 es desde arriba, positivo es una perspectiva inclinada hacia adelante, negativo es una vista en elevación que mira por encima del horizonte.
paddings
Área oculta por la interfaz de usuario. Los cálculos de centro y ajuste se realizan restando esta área.
visibleRegion
El área terrestre realmente visible en la pantalla. Tiene un límite rectangular y las coordenadas de las cuatro esquinas.

Con copy() se pueden reemplazar solo partes; la comparación se realiza con una tolerancia de zoom 0,01 y inclinación 0,01. Así se evita que fluctuaciones menores de los valores devueltos por el proveedor se interpreten erróneamente como "movido".

Plataforma

03 · Unificación de nivel de zoom = altura

El zoom se trata internamente igual que la “altura de la cámara”. En `core` hay convertidores que pueden convertir mutuamente el zoom de teselas 2D y la altura/distancia de visión 3D; los valores de todos los proveedores se convierten primero a un zoom común (estándar de Google Maps) antes de publicarse.

Este estándar no se decidió solo por teoría. Con la herramienta de calibración de la app de ejemplo, se midió el área realmente visible para cada motor al especificar el mismo zoom, y se calibró la altura estándar para que las áreas coincidan. Por eso, el zoom 14 tiene el mismo ancho en todos los proveedores.

Figura · niveles de zoom y la escala que muestran (cuántos km caben aproximadamente a lo ancho de la pantalla)
0
≈ 40,000 km
Todo el globo
El mundo entero en una vista: solo los contornos de los continentes.
3
≈ 5,000 km
Continente
Continentes y océanos; las fronteras todavía no significan nada.
6
≈ 600 km
País
Un país entero y dónde están sus ciudades.
10
≈ 40 km
Área metropolitana
Zonas urbanizadas y vías principales: una vista inicial habitual.
14
≈ 2.5 km
Distrito
La trama de calles es legible; adecuada para una lista de pines de tiendas.
17
≈ 300 m
Manzana
Cruces y huellas de edificios: navegación a pie.
20
≈ 40 m
Edificio
Entradas y posiciones exactas.
Cada paso duplica la escala. Las cifras son para el ecuador con 256 px de ancho de pantalla; la distancia real en el suelo cambia con la latitud.
LATITUDE

El mismo zoom necesita una altura distinta según la latitud

Web Mercator amplía el mapa hacia los polos, así que la altitud de cámara para un mismo nivel de zoom cambia varias veces entre el ecuador y latitudes altas. La conversión zoom ⇄ altitud ya lo tiene en cuenta.

ZOOM 10 · cerca del ecuador
≈ 5,200 m
ZOOM 10 · alrededor de 70° N
≈ 1,700 m
NATIVE
Alinear los valores nativos a esta escala
±0.0
Google Maps · Mapbox 2D — Se toma como referencia. La escala de zoom unificada sigue esta familia.
+1.0
MapLibre · MapTiler · TomTom — El zoom nativo está un paso más abajo, por eso se aplica un desplazamiento de +1.0.
m
MapKit · ArcGIS · Cesium · Google 3D — Se convierte entre zoom y altitud/distancia, teniendo en cuenta la latitud y la inclinación.
ZOOM RANGE
0.0 – 22.0
ZOOM FACTOR
2.0
ZOOM 0 ALTITUDE
171,319,879 m
MAPLIBRE OFFSET
+1.0

Lo único constante es la relación “un paso por cada duplicación” y la altura del zoom 0 como referencia. También se incluye en la conversión el hecho de que la distancia en el suelo con el mismo zoom se reduce a latitudes más altas, por lo que la escala no se desajusta entre Tokio y Honolulu.

04 · Soporte pseudo para tilt < 0

tilt acepta valores de −60 a 60 grados. Los valores positivos son la vista en ángulo habitual, mientras que los valores negativos son una vista de elevación mirando por encima del horizonte. Ningún motor de mapas puede expresar esto directamente; todos los SDK solo aceptan pitch ≥ 0.

Por eso, MapConductor reemplaza esto manteniendo la posición y la orientación de la cámara, pero pasándole al motor un valor de inclinación positivo. Desde la app se puede usar tilt negativo tal cual, y la posición de cámara obtenida también se devuelve como tilt negativo.

tilt = 0

Un plano visto desde directamente arriba. Un rectángulo en la superficie terrestre se convierte directamente en un rectángulo en la pantalla.

tilt > 0

Una perspectiva en diagonal inclinada hacia adelante. En la superficie terrestre se convierte en un trapecio más estrecho en la parte cercana y más ancho en la parte lejana.

tilt < 0

Una vista elevada por encima del horizonte. Un punto de vista que incluye el cielo, que no existe de forma nativa.

Cómo funciona la emulación
01
La posición del ojo y la altura de la cámara no se mueven; solo el punto objetivo en el suelo avanza siguiendo el bearing, en una medida derivada de la altitud y el ángulo de inclinación.
02
Luego se pasa a la engine abs(tilt) como inclinación hacia abajo. Como el ojo y su dirección coinciden, la imagen es igual a la vista hacia arriba. Las engines de la familia MapLibre reciben factores de corrección fijos sobre el desplazamiento y el zoom para que el encuadre también coincida.
03
Al leer la cámara se recorre el mismo cálculo a la inversa, deshaciendo el desplazamiento y el ajuste de zoom para devolver la inclinación negativa lógica y el centro original. Los viajes de ida y vuelta son estables.
Compatibilidad con inclinación negativa
Android · todos los proveedores
OK
iOS · todos los proveedores
OK
React · todos menos Google Maps 2D
OK
React · Google Maps 2D
N/A

La única excepción es Google Maps 2D en React: el mapa ráster de la API de JavaScript. Su inclinación depende del navegador y de las teselas, así que no se pueden controlar la altitud ni la orientación en que se apoya la emulación. Google Maps 3D (Map3DElement) sí admite inclinación negativa.

TiltMapPageViewModel.kt
// Pasa el deslizador de −60 a 60 tal cual; un valor negativo es la misma llamada
currentPosition = currentPosition.copy(tilt = tilt)
mapViewState.value?.moveCameraTo(currentPosition)
Video de ejemplo · Tilt (mover −60〜60 de forma continua)

05 · VisibleRegion — el área realmente visible

Al inclinar la cámara, el rectángulo de la pantalla se convierte en un trapecio en el suelo. Un rectángulo que solo incluye sudoeste y noreste (bounds) no puede expresar la diferencia de ancho entre el primer plano y el fondo. VisibleRegion tiene, además del rectángulo, las coordenadas en el suelo de las cuatro esquinas de la pantalla, lo que permite manejar “el área realmente mostrada” incluso con cámara inclinada.

near es el borde inferior de la pantalla, es decir, el lado más cercano a la cámara; far es el borde superior, el lado más alejado. left / right están basados en la pantalla, por lo que su significado no cambia aunque se gire el mapa con bearing. Gracias a estos cuatro puntos, incluso con mapa inclinado se pueden determinar correctamente el área de obtención de datos y la atribución de fuentes.

farLeft
farRight
nearLeft
nearRight
rectángulo en pantalla → trapecio en el suelo
El borde lejano es más ancho y el cercano más estrecho. Bounds es la caja que rodea ese trapecio.
bounds
La caja suroeste/noreste que rodea el trapecio, para comprobaciones aproximadas y APIs existentes.
nearLeft / nearRight
Coordenadas en el suelo de las esquinas inferiores de la pantalla: el lado cercano.
farLeft / farRight
Coordenadas en el suelo de las esquinas superiores: el lado lejano, que se abre más cuanto más se inclina.
Para qué sirve
Cargar datos del área visible, fitBounds y cambiar las reglas de atribución de teselas.
VisibleRegionViewModel.kt
mapViewState.cameraPosition.visibleRegion?.let { region ->
    val sw = region.bounds.southWest
    val ne = region.bounds.northEast
    // Las cuatro esquinas de una pantalla inclinada, tratables como un trapecio
    val corners = listOf(region.nearLeft, region.nearRight, region.farLeft, region.farRight)
}

Páginas relacionadas