# Mapas

> MapContainer, pins, formas, rutas y utilidades — con las trampas de clave, null y thread.

Source: https://crossfly.dev/es/docs/data-and-services/maps — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# Mapas

Mapa nativo integrado con **`MapContainer`** (un `Component`) sobre la interfaz `Map`. Soporta Google y
OSM como engines.

## ⚠️ Cuatro trampas que evitan bugs
1. **Configure la clave** (estática) antes de crear el mapa: `MapContainer.googleMapsKey = "...";`.
2. **`map` puede ser `null`** — acceda a `mc.map` **solo después** del `onMapLoad` (y trate `null`: Web/TeaVM
   **no soporta** mapa nativo).
3. **Todas las operaciones de mapa en la render (paint) thread** — regrese con `onUI(...)` si viene de fondo.
4. La antigua `phone.GoogleMaps` (visualizador externo) **fue eliminada**.

## Crear y centrar
```java
MapContainer.googleMapsKey = "SUA_CHAVE";

MapContainer mc = new MapContainer(Map.MapEngine.GOOGLE, 0, () -> {   // onMapLoad
   if (mc.map == null) return;                          // Web/TeaVM: sin mapa nativo
   mc.map.centerOnLocation(new LatLon(-23.55, -46.63), 14);
   mc.map.setMapType(Map.LayerType.NORMAL);
});
tela.add(LAY_FILL, mc);
```
`MapEngine`: `GOOGLE` · `OSM`. `LayerType`: `NORMAL` · `TERRAIN` · `HYBRID` · `SATELLITE`.

## Pins (API fluida)
```java
mc.map.add(new MapPin()
   .position(-23.55, -46.63)
   .title("São Paulo")
   .snippet("Centro")
   .draggable(false)
   .onClick(p -> { Hint.show(p.title, 2000); return true; }));   // true = suprime info window
```

## Formas — `MapShape.Line/Polygon/Circle`
```java
// círculo de 500 m (colores en ARGB 0xAARRGGBB)
MapShape.Circle c = new MapShape.Circle(2, 0xFF2196F3, 0x402196F3);
c.setPolygon(new LatLon(-23.55, -46.63), 500.0);   // centro + radio en metros
mc.map.add(c);
```
`Line(thick, color)` y `Polygon(borderWidth, borderColor, fillColor)` siguen el mismo patrón.

## Rutas y utilidades
```java
double metros = MapUtils.distanceInMeters(new LatLon(-23.55,-46.63), new LatLon(-22.90,-43.20));
Image thumb = MapUtils.getMapImage(-23.55, -46.63, false, 13, 600, 400);   // imagen estática (null si error/sin clave)
```
Las direcciones reales (HTTP + **Jackson**) vienen de `DirectionsFetcher` (callback `onRouteReady(List<LatLon>)`),
combinado con `MapRoute(lineShape, pins)` — marshalice el resultado con `onUI(...)`. Detalle en el
Capítulo 25.

## Buenas prácticas
- Clave **antes** de instanciar; acceso a `mc.map` **solo** en el `onMapLoad`.
- Trate `mc.map == null` (Web) y `getMapImage(...) == null`.
- Operaciones de mapa en la render thread; resultados de red vía `onUI(...)`.

## Errores comunes
- Acceder a `mc.map` en el constructor (aún `null`).
- Olvidar `MapContainer.googleMapsKey`.
- Usar `phone.GoogleMaps` (eliminado).

## Vea también
- [Localización y GPS](/docs/data-and-services/device-location-gps) · [LocationView](/docs/ui/widgets/locationview)
