¿Qué hace Mapas y Geolocalización?
Reglas
- Mapbox GL JS para mapas interactivos enriquecidos —
react-map-gl como envoltorio para React
- Leaflet como alternativa gratuita —
react-leaflet para React, sin necesidad de clave API para los mapas base
- Gestión de claves API:
NEXT_PUBLIC_MAPBOX_TOKEN para Mapbox del lado del cliente, nunca exponer secretos del lado del servidor
- Geocodificación: convertir direcciones en coordenadas — Mapbox Geocoding API o OpenStreetMap Nominatim (gratuito)
- Agrupación de marcadores (clustering): usar
supercluster o la agrupación integrada de Mapbox para conjuntos grandes de marcadores (100+)
- Patrón de localizador de tiendas: geocodificar la ubicación del usuario → consultar tiendas cercanas con Haversine/PostGIS → mostrar en el mapa
- Visualización de rutas: Mapbox Directions API para rutas en coche/a pie, renderizar como capa GeoJSON LineString
- Geolocalización del navegador:
navigator.geolocation.getCurrentPosition() — siempre manejar la denegación de permisos con elegancia
- Cargar el componente de mapa de forma diferida (lazy load) — los mapas son pesados; usar
dynamic(() => import("./Map"), { ssr: false })
Patrones
import Map, { Marker, NavigationControl } from "react-map-gl";
import "mapbox-gl/dist/mapbox-gl.css";
function StoreMap({ stores }: { stores: Store[] }) {
return (
<Map
initialViewState={{ longitude: -122.4, latitude: 37.8, zoom: 11 }}
mapboxAccessToken={process.env.NEXT_PUBLIC_MAPBOX_TOKEN}
mapStyle="mapbox://styles/mapbox/streets-v12"
style={{ width: "100%", height: 400 }}
>
<NavigationControl position="top-right" />
{stores.map((store) => (
<Marker key={store.id} longitude={store.lng} latitude={store.lat}>
<Pin />
</Marker>
))}
</Map>
);
}
function haversineDistance(lat1: number, lon1: number, lat2: number, lon2: number): number {
const R = 6371;
const dLat = ((lat2 - lat1) * Math.PI) / 180;
const dLon = ((lon2 - lon1) * Math.PI) / 180;
const a = Math.sin(dLat / 2) ** 2 + Math.cos((lat1 * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180) * Math.sin(dLon / 2) ** 2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
Evitar
- Renderizar componentes de mapa en el lado del servidor — siempre cargar con
ssr: false o "use client"
- Renderizar más de 1000 marcadores sin agrupación (clustering) — mata el rendimiento; usar supercluster
- Hardcodear coordenadas — geocodificar direcciones en tiempo de compilación o bajo demanda
- Ignorar la denegación de permisos de geolocalización — siempre proporcionar una alternativa (ingreso manual de dirección)