0. 🎯 Introducción - ¿Qué vamos a lograr?
🏆 Objetivo del Proyecto:
Crear un Sistema Completo de Gestión de Bibliotecas con Geolocalización Automática que permita:
- ✅ Registrar bibliotecas ingresando solo la dirección postal
- ✅ Geocodificar automáticamente: Convertir direcciones en coordenadas GPS (latitud/longitud) usando Google Maps API
- ✅ Almacenar en MySQL: Guardar toda la información de bibliotecas con sus coordenadas
- ✅ Mapa interactivo: Mostrar las bibliotecas en un mapa de Google Maps con marcadores clicables
- ✅ API REST completa: Crear endpoints para listar, crear, editar y eliminar bibliotecas
- ✅ Búsqueda de cercanas: Encontrar bibliotecas próximas a una ubicación específica
- ✅ Panel de administración: Gestionar bibliotecas desde interfaz visual de Django
- ✅ Enlaces directos a Google Maps: Botones para abrir cada biblioteca en la app de Google Maps
🎬 ¿Cómo Funciona el Sistema Completo?
Flujo de Uso Final:
- Administrador ingresa al panel: Accede a
http://localhost:8000/admin/ - Crea una biblioteca: Ingresa nombre, dirección, ciudad, país, teléfono, horario
- Django geocodifica automáticamente: Llama a Google Maps API y obtiene:
- Latitud y Longitud
- Place ID único de Google
- URL de Google Maps
- Datos se guardan en MySQL: Información completa almacenada
- Usuario final ve el mapa: Accede a
http://localhost:8000/api/mapa/ - Mapa carga bibliotecas: Hace petición a la API REST
- Marcadores aparecen: Cada biblioteca tiene un pin en su ubicación exacta
- Usuario hace clic en marcador: Ventana emergente muestra información detallada
- Usuario abre en Google Maps: Botón abre navegación en app de Google Maps
⚙️ ¿Qué Hace Cada Tecnología?
| Componente | Función Específica en el Sistema | ¿Por qué lo usamos? |
|---|---|---|
| Django | Framework backend que gestiona lógica de negocio, rutas, modelos y conexión a BD | Ahorra código: incluye admin, ORM, autenticación listos |
| MySQL | Base de datos relacional que almacena bibliotecas y coordenadas | Rápida, robusta, soporta geoespacial, compatible con Django |
| Google Maps Geocoding API | Convierte "Av. Universidad 3000, CDMX" en latitud/longitud | Precisión mundial, base de datos actualizada, 40,000 peticiones/mes gratis |
| Google Maps JavaScript API | Renderiza mapa interactivo en el navegador con zoom, marcadores, popups | Líder del mercado, familiar para usuarios, bien documentada |
| Django REST Framework | Crea API REST con endpoints JSON para listar/crear bibliotecas | Interfaz web automática, serialización, paginación incluidas |
| googlemaps (Python) | Cliente Python que facilita llamadas a Google Maps API | Simplifica código: una línea en lugar de configurar HTTP/JSON |
🛠️ Tecnologías que usaremos (EXPLICADAS):
| Tecnología | Para qué se usa | Versión |
|---|---|---|
| Python 3.8+ | Lenguaje de programación del backend (servidor) | 3.8+ |
| Django 4.2 | Framework web que maneja rutas, modelos, admin, ORM | 4.2 |
| MySQL 8.0 | Base de datos relacional para almacenar bibliotecas y coordenadas | 8.0+ |
| mysqlclient | Conector Python-MySQL (permite a Django comunicarse con MySQL) | Latest |
| googlemaps 4.10 | Cliente Python oficial de Google Maps API (geocodificación backend) | 4.10.0 |
| Google Maps Geocoding API | Servicio cloud que convierte direcciones en coordenadas GPS | v3 |
| Google Maps JavaScript API | Renderiza mapas interactivos en el navegador (frontend) | Latest |
| Django REST Framework 3.14 | Crea API REST con endpoints JSON automáticos | 3.14+ |
| HTML5/CSS3/JavaScript | Frontend: mapa interactivo, estilos, interactividad | ES6+ |
📊 Arquitectura del Sistema (Diagrama de Componentes)
┌─────────────────────┐ ┌──────────────────────┐
│ NAVEGADOR │ │ GOOGLE MAPS API │
│ (Frontend) │ │ (Cloud) │
│ │ │ │
│ - HTML/CSS │ │ - Geocoding API │
│ - JavaScript │ │ - JavaScript API │
│ - Google Maps SDK │ │ - Places API │
└──────────┬──────────┘ └──────────┬───────────┘
│ │
│ HTTP GET /api/mapa/ │ HTTPS
│ HTTP GET /api/bibliotecas/ │ Geocode Request
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ SERVIDOR DJANGO (Backend) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌─────────────────────┐ │
│ │ views.py │ │ api_views.py │ │ services.py │ │
│ │ │ │ │ │ │ │
│ │ mapa_ │ │ Biblioteca │ │ GoogleMapsService │ │
│ │ bibliotecas │ │ ViewSet │ │ .geocodificar() │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬──────────────┘ │
│ │ │ │ │
│ │ │ │ │
│ ┌──────▼──────────────────▼──────────────────▼──────────────┐ │
│ │ models.py (Biblioteca) │ │
│ │ - nombre, direccion, ciudad, pais │ │
│ │ - latitud, longitud, place_id │ │
│ │ - google_maps_url, telefono, horario │ │
│ └──────┬─────────────────────────────────────────────────────┘ │
│ │ Django ORM │
└─────────┼────────────────────────────────────────────────────────┘
│ SQL Queries
▼
┌──────────────────────────┐
│ MYSQL DATABASE │
│ │
│ Tabla: libros_biblioteca│
│ - id (PK) │
│ - nombre │
│ - direccion │
│ - latitud │
│ - longitud │
│ - place_id │
│ - google_maps_url │
│ - telefono │
│ - horario │
└──────────────────────────┘
FLUJO DE DATOS:
1. Usuario → Navegador → Django: GET /api/mapa/
2. Django → Navegador: Envía mapa.html
3. JavaScript → Django: GET /api/bibliotecas/ (petición AJAX)
4. Django → MySQL: SELECT * FROM libros_biblioteca
5. MySQL → Django: Datos de bibliotecas
6. Django → JavaScript: JSON con bibliotecas
7. JavaScript → Google Maps SDK: Renderizar mapa
8. Google Maps SDK → Navegador: Mapa interactivo visible
GEOCODIFICACIÓN (cuando se crea biblioteca):
1. Admin → Django: POST biblioteca (solo dirección)
2. Django → Google Geocoding API: "Av. Universidad 3000, CDMX"
3. Google → Django: {lat: 19.3326, lng: -99.1876, place_id: "ChIJ..."}
4. Django → MySQL: INSERT biblioteca con coordenadas
5. MySQL → Django: Confirmación
6. Django → Admin: Biblioteca creada ✅
⏱️ Tiempo Estimado Total: 2-3 horas
| Fase | Tiempo | Qué harás |
|---|---|---|
| PASO 1: API Key | 15 min | Crear cuenta Google Cloud, obtener API Key, habilitar facturación ($200 gratis) |
| PASO 2: Proyecto Django | 20 min | Crear proyecto, entorno virtual, instalar dependencias, configurar settings.py |
| PASO 3: MySQL | 15 min | Crear base de datos, configurar conexión, probar conectividad |
| PASO 4: Código Backend | 40 min | Crear models.py, services.py, admin.py, migraciones |
| PASO 5: Panel Admin | 10 min | Registrar modelos, personalizar admin, crear superusuario |
| PASO 6: API REST | 25 min | Serializers, ViewSets, URLs, probar endpoints |
| PASO 7: Mapa Frontend | 30 min | HTML, JavaScript, integración Google Maps SDK, marcadores interactivos |
| Pruebas y Verificación | 15 min | Crear bibliotecas, verificar coordenadas, probar mapa, Muestras de pantalla |
💡 Antes de Empezar:
- 📝 Lee toda la guía primero para entender el flujo completo
- 🖥️ Abre 2 ventanas: una para la guía y otra para trabajar y 1 que sera donde te ves en el video
- ⚠️ Si algo no funciona, NO te saltes pasos, vuelve a revisar
- 🆘 Si tienes errores, lee las secciones de advertencias (⚠️)
PASOS 1-7: Práctica Guiada Completa
📋 ORDEN DE EJECUCIÓN - Sigue estos pasos EN ORDEN:
Esta sección contiene TODOS los pasos prácticos para crear el proyecto.
Los pasos están numerados y organizados. NO te saltes ninguno.
- PASO 1: Obtener API Key de Google Maps (encuentra este título abajo ⬇️)
- PASO 2: Crear Proyecto Django
- PASO 3: Configurar MySQL
- PASO 4: Escribir el Código (models, services, admin)
- PASO 5: Panel de Administración
- PASO 6: Crear API REST (continúa en la siguiente sección principal)
- PASO 7: Mapa Interactivo (continúa en sección Frontend)
👇 Empieza con el PASO 1 que está justo debajo de esta advertencia
2. 🌍 5 Bibliotecas/APIs de Geolocalización Más Populares
📚 En esta sección aprenderás sobre:
- ✅ Las 5 plataformas de geolocalización líderes en el mercado
- ✅ Comparación de características, precios y casos de uso
- ✅ Ejemplos de código funcionales para cada una
- ✅ Cuándo usar cada biblioteca según tu proyecto
1. 🗺️ Google Maps Platform (La Más Completa)
Descripción: Plataforma líder mundial con APIs para mapas, geocodificación, rutas, lugares y más.
✅ Ventajas:
- Cobertura global: Datos precisos en todo el mundo
- Ecosistema completo: Geocoding, Directions, Places, Street View
- Documentación excelente: Miles de ejemplos y tutoriales
- Familiaridad: Los usuarios conocen la interfaz de Google Maps
- Gratis para empezar: $200 USD/mes de créditos + 40,000 geocodificaciones gratis
⚠️ Desventajas:
- Costoso para alto volumen: Después de los créditos gratis, puede ser caro
- Requiere tarjeta de crédito: Incluso para la versión gratuita
- Lock-in: Difícil migrar a otra plataforma después
| API | Funcionalidad | Casos de Uso |
|---|---|---|
| Maps JavaScript API | Mapas interactivos en web | Mostrar ubicaciones, marcadores |
| Geocoding API | Convertir direcciones ↔ coordenadas | Búsqueda de direcciones |
| Geolocation API | Ubicación por WiFi/Cell | Detectar ubicación del usuario |
| Places API | Información de lugares | Buscar restaurantes, hoteles |
| Directions API | Calcular rutas | Navegación, estimación de tiempo |
| Distance Matrix API | Distancias entre múltiples puntos | Logística, delivery |
import googlemaps
# Inicializar cliente (este es el que usamos en la práctica)
gmaps = googlemaps.Client(key='TU_API_KEY')
# Geocodificar dirección → coordenadas
resultado = gmaps.geocode('Av. Universidad 3000, Ciudad de México')
if resultado:
ubicacion = resultado[0]['geometry']['location']
print(f"Latitud: {ubicacion['lat']}") # 19.3326
print(f"Longitud: {ubicacion['lng']}") # -99.1876
# Geocodificación inversa: coordenadas → dirección
reverso = gmaps.reverse_geocode((19.4326, -99.1332))
direccion = reverso[0]['formatted_address']
print(direccion) # "Ciudad de México, CDMX, México"
# Calcular ruta
ruta = gmaps.directions(
origin='Zócalo, CDMX',
destination='Chapultepec, CDMX',
mode='driving'
)
distancia = ruta[0]['legs'][0]['distance']['text']
print(f"Distancia: {distancia}") # "5.2 km"
💡 Cuándo usar Google Maps:
- ✅ Proyectos empresariales con presupuesto
- ✅ Apps que necesitan Street View
- ✅ Cuando la precisión global es crítica
- ✅ Proyectos donde los usuarios esperan la interfaz de Google
2. 🎨 Mapbox (La Más Personalizable)
Descripción: Plataforma de mapas con enfoque en diseño personalizado y rendimiento para apps móviles.
✅ Ventajas:
- Diseño personalizado: Mapas con estilos únicos (oscuros, satélite, 3D)
- Rendimiento excelente: Optimizado para móviles y web
- Precio competitivo: Hasta 50,000 peticiones gratis/mes
- Navegación turn-by-turn: Incluida en el plan gratuito
- SDK nativo: Android, iOS, Unity, React Native
⚠️ Desventajas:
- Menor cobertura POI: Menos puntos de interés que Google
- Documentación compleja: Curva de aprendizaje más alta
- Sin Street View: No tiene vista de calle
// HTML: <div id="map" style="height: 500px;"></div>
// Cargar: <script src='https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.js'></script>
mapboxgl.accessToken = 'TU_MAPBOX_TOKEN';
// Crear mapa con estilo personalizado
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/dark-v11', // Estilo oscuro
center: [-99.1332, 19.4326], // [lng, lat] CDMX
zoom: 12
});
// Agregar marcador personalizado
const marker = new mapboxgl.Marker({
color: '#FF0000', // Color rojo
draggable: true // Marcador movible
})
.setLngLat([-99.1332, 19.4326])
.setPopup(new mapboxgl.Popup().setHTML('<h3>Ciudad de México</h3>'))
.addTo(map);
// Escuchar cuando arrastran el marcador
marker.on('dragend', () => {
const lngLat = marker.getLngLat();
console.log(`Nueva posición: ${lngLat.lng}, ${lngLat.lat}`);
});
import requests
MAPBOX_TOKEN = 'TU_MAPBOX_TOKEN'
direccion = 'Av. Universidad 3000, Ciudad de México'
# Geocodificar dirección
url = f'https://api.mapbox.com/geocoding/v5/mapbox.places/{direccion}.json'
params = {'access_token': MAPBOX_TOKEN}
response = requests.get(url, params=params)
data = response.json()
if data['features']:
coords = data['features'][0]['geometry']['coordinates']
print(f"Longitud: {coords[0]}, Latitud: {coords[1]}")
💡 Cuándo usar Mapbox:
- ✅ Apps móviles con diseño único
- ✅ Startups con presupuesto limitado
- ✅ Juegos y aplicaciones 3D
- ✅ Cuando necesitas mapas oscuros/satélite personalizados
3. 🆓 Leaflet + OpenStreetMap (La Más Libre)
Descripción: Biblioteca JavaScript open-source para mapas interactivos, usa datos de OpenStreetMap (colaborativo).
✅ Ventajas:
- 100% Gratis: Sin límites de peticiones, sin tarjeta de crédito
- Open Source: Código abierto, sin vendor lock-in
- Ligero: Solo 42 KB de JavaScript
- Plugins: Miles de extensiones de la comunidad
- Datos comunitarios: OpenStreetMap es editado por millones de usuarios
⚠️ Desventajas:
- Sin geocodificación nativa: Necesitas servicios externos (Nominatim)
- Menos POIs: Datos de lugares menos completos que Google
- Calidad variable: Datos dependen de la comunidad (mejor en Europa/USA)
- Sin soporte oficial: Depende de la comunidad
<!-- Cargar Leaflet -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<!-- Contenedor del mapa -->
<div id="map" style="height: 500px;"></div>
<script>
// Crear mapa centrado en CDMX
const map = L.map('map').setView([19.4326, -99.1332], 13);
// Agregar capa de OpenStreetMap
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
// Agregar marcador
L.marker([19.4326, -99.1332])
.addTo(map)
.bindPopup('<b>Ciudad de México</b><br>Zócalo')
.openPopup();
// Agregar círculo (radio de 1 km)
L.circle([19.4326, -99.1332], {
color: 'red',
fillColor: '#f03',
fillOpacity: 0.3,
radius: 1000 // metros
}).addTo(map);
</script>
from geopy.geocoders import Nominatim
from geopy.distance import geodesic
# Inicializar geocodificador (GRATIS)
geolocator = Nominatim(user_agent="mi_app_biblioteca")
# Geocodificar dirección
ubicacion = geolocator.geocode("Av. Universidad 3000, Ciudad de México")
print(f"Latitud: {ubicacion.latitude}")
print(f"Longitud: {ubicacion.longitude}")
# Geocodificación inversa
ubicacion_inversa = geolocator.reverse("19.4326, -99.1332")
print(ubicacion_inversa.address)
# Calcular distancia entre dos puntos (Haversine)
punto1 = (19.4326, -99.1332) # Zócalo
punto2 = (19.4260, -99.1679) # Chapultepec
distancia = geodesic(punto1, punto2).kilometers
print(f"Distancia: {distancia:.2f} km") # 3.85 km
💡 Cuándo usar Leaflet + OpenStreetMap:
- ✅ Proyectos open-source sin presupuesto
- ✅ Apps educativas o sin fines de lucro
- ✅ Proyectos donde no quieres depender de Google
- ✅ Prototipos y MVPs rápidos
4. 🚗 HERE Maps (La Mejor para Logística)
Descripción: Plataforma de mapas de nivel empresarial, especializada en navegación y logística (usado por BMW, Mercedes).
✅ Ventajas:
- Tráfico en tiempo real: Datos de tráfico muy precisos
- Rutas optimizadas: Excelente para flotas y delivery
- Mapas offline: Descarga mapas completos
- Gratis hasta 250,000 peticiones/mes: Muy generoso
- Cobertura automotriz: Datos de velocidad, restricciones de camiones
⚠️ Desventajas:
- Menos conocido: Usuarios no están familiarizados
- Documentación compleja: Enfocada a empresas
- UI menos atractiva: Diseño más funcional que visual
// Cargar HERE SDK
// <script src="https://js.api.here.com/v3/3.1/mapsjs-core.js"></script>
const platform = new H.service.Platform({
'apikey': 'TU_HERE_API_KEY'
});
// Obtener servicios de mapa
const defaultLayers = platform.createDefaultLayers();
// Crear mapa
const map = new H.Map(
document.getElementById('map'),
defaultLayers.vector.normal.map,
{
center: {lat: 19.4326, lng: -99.1332},
zoom: 13
}
);
// Calcular ruta con tráfico
const router = platform.getRoutingService(null, 8);
const routeParams = {
'routingMode': 'fast', // Ruta más rápida
'transportMode': 'car',
'origin': '19.4326,-99.1332',
'destination': '19.4260,-99.1679',
'return': 'polyline,summary'
};
router.calculateRoute(routeParams, (result) => {
const route = result.routes[0];
console.log(`Distancia: ${route.sections[0].summary.length} metros`);
console.log(`Duración: ${route.sections[0].summary.duration} segundos`);
}, console.error);
💡 Cuándo usar HERE Maps:
- ✅ Apps de delivery y logística
- ✅ Rastreo de flotas de vehículos
- ✅ Apps de navegación automotriz
- ✅ Cuando necesitas datos de tráfico precisos
5. 🐍 Geopy (La Mejor para Python)
Descripción: Biblioteca Python que unifica múltiples servicios de geocodificación (Google, Nominatim, Bing, etc.).
✅ Ventajas:
- Multi-proveedor: Un código para todos los servicios (Google, OSM, Bing, MapBox)
- API consistente: Mismo código, diferentes backends
- Funciones geoespaciales: Calcular distancias, rutas, áreas
- Gratis con Nominatim: Sin límites usando OpenStreetMap
- Fácil de usar: 3 líneas de código para geocodificar
⚠️ Desventajas:
- Solo backend: No incluye mapas visuales (necesitas Leaflet/Folium)
- Rate limiting: Nominatim limita 1 petición/segundo
- No tiene todas las APIs: Falta Places, Directions avanzadas
from geopy.geocoders import Nominatim, GoogleV3, Bing
from geopy.distance import geodesic, great_circle
from geopy.exc import GeocoderTimedOut
direccion = "Av. Universidad 3000, Ciudad de México"
# 1. Geocodificar con OpenStreetMap (GRATIS)
geolocator_osm = Nominatim(user_agent="mi_app")
ubicacion_osm = geolocator_osm.geocode(direccion)
print(f"OSM: {ubicacion_osm.latitude}, {ubicacion_osm.longitude}")
# 2. Geocodificar con Google Maps (PAGO)
geolocator_google = GoogleV3(api_key="TU_GOOGLE_API_KEY")
ubicacion_google = geolocator_google.geocode(direccion)
print(f"Google: {ubicacion_google.latitude}, {ubicacion_google.longitude}")
# 3. Geocodificar con Bing Maps (PAGO)
geolocator_bing = Bing(api_key="TU_BING_API_KEY")
ubicacion_bing = geolocator_bing.geocode(direccion)
print(f"Bing: {ubicacion_bing.latitude}, {ubicacion_bing.longitude}")
# Calcular distancia (2 fórmulas diferentes)
punto1 = (19.4326, -99.1332)
punto2 = (19.4260, -99.1679)
distancia_geodesic = geodesic(punto1, punto2).kilometers # Más preciso (elipsoide)
distancia_circle = great_circle(punto1, punto2).kilometers # Más rápido (esfera)
print(f"Distancia (geodesic): {distancia_geodesic:.2f} km")
print(f"Distancia (círculo): {distancia_circle:.2f} km")
from geopy.geocoders import Nominatim
import time
geolocator = Nominatim(user_agent="mi_app")
direcciones = [
"Biblioteca Vasconcelos, CDMX",
"Biblioteca Central UNAM",
"Biblioteca de México José Vasconcelos"
]
resultados = []
for direccion in direcciones:
try:
ubicacion = geolocator.geocode(direccion)
if ubicacion:
resultados.append({
'direccion': direccion,
'lat': ubicacion.latitude,
'lng': ubicacion.longitude
})
print(f"✅ {direccion}: {ubicacion.latitude}, {ubicacion.longitude}")
else:
print(f"❌ No encontrado: {direccion}")
except GeocoderTimedOut:
print(f"⏱️ Timeout: {direccion}")
time.sleep(1) # ⚠️ Respetar límite de Nominatim (1 req/sec)
# Guardar en DataFrame (Pandas)
import pandas as pd
df = pd.DataFrame(resultados)
df.to_csv('bibliotecas_geocodificadas.csv', index=False)
print("✅ Guardado en CSV")
💡 Cuándo usar Geopy:
- ✅ Scripts de análisis de datos (Jupyter, Pandas)
- ✅ Geocodificación en batch de muchas direcciones
- ✅ Backends Python que no necesitan mapas visuales
- ✅ Proyectos donde quieres flexibilidad de proveedor
📊 Comparación Final: ¿Cuál elegir?
| Plataforma | Precio | Límite Gratis | Mejor Para | Peor Para |
|---|---|---|---|---|
| Google Maps | $$ (caro) | $200/mes + 40K geocodes | Apps empresariales, precisión global | Startups sin presupuesto |
| Mapbox | $ (moderado) | 50K peticiones/mes | Apps móviles, diseño custom | Necesitas Street View |
| Leaflet + OSM | GRATIS | Ilimitado | Proyectos open-source, prototipos | Apps que necesitan POIs detallados |
| HERE Maps | $$ (moderado) | 250K peticiones/mes | Logística, flotas, tráfico | Apps B2C generales |
| Geopy | Depende del proveedor | Ilimitado (Nominatim) | Análisis de datos Python | Necesitas mapas visuales |
🏆 Recomendación General:
- Prototipo/MVP: Leaflet + Nominatim (gratis total)
- Startup con presupuesto: Mapbox (buen balance precio/calidad)
- Empresa grande: Google Maps (mejor cobertura y soporte)
- Logística: HERE Maps (mejor tráfico y rutas)
- Análisis backend: Geopy (flexible y potente)
📊 Ejemplo de Flujo de Información:
- Usuario ingresa dirección en la aplicación
- Frontend envía petición a Django
- Django llama a Google Maps Geocoding API
- Google devuelve coordenadas en formato JSON
- Django guarda en MySQL y responde al frontend
- Frontend muestra el mapa con la ubicación
2.2 Datos que Intercambian las APIs (Explicación Detallada)
Las APIs de geolocalización intercambian información en formato JSON (JavaScript Object Notation), un formato de texto legible tanto para humanos como para máquinas.
{
"results": [
{
// Dirección formateada por Google (estandarizada)
"formatted_address": "Biblioteca Vasconcelos, Eje 1 Norte, Buenavista, 06350 Ciudad de México, CDMX, México",
// Información geométrica (coordenadas y tipo de ubicación)
"geometry": {
"location": {
"lat": 19.4395418, // Latitud (Norte-Sur: -90 a +90)
"lng": -99.1520794 // Longitud (Este-Oeste: -180 a +180)
},
// Tipo de precisión de la ubicación
"location_type": "ROOFTOP", // ROOFTOP (preciso) | RANGE_INTERPOLATED | GEOMETRIC_CENTER | APPROXIMATE
// Área rectangular que contiene toda la ubicación
"viewport": {
"northeast": { "lat": 19.4408907, "lng": -99.1507304 },
"southwest": { "lat": 19.4381927, "lng": -99.1534284 }
},
// Área precisa del lugar (opcional)
"bounds": {
"northeast": { "lat": 19.4408907, "lng": -99.1507304 },
"southwest": { "lat": 19.4381927, "lng": -99.1534284 }
}
},
// ID único de Google Maps para este lugar
"place_id": "ChIJXxQBnbP_0YURW9JOEKiHlBM", // Identificador permanente
// Componentes individuales de la dirección
"address_components": [
{
"long_name": "Biblioteca Vasconcelos",
"short_name": "Biblioteca Vasconcelos",
"types": ["establishment", "library", "point_of_interest"]
},
{
"long_name": "Eje 1 Norte",
"short_name": "Eje 1 Nte",
"types": ["route"]
},
{
"long_name": "Buenavista",
"short_name": "Buenavista",
"types": ["neighborhood", "political"]
},
{
"long_name": "Ciudad de México",
"short_name": "CDMX",
"types": ["locality", "political"]
},
{
"long_name": "México",
"short_name": "MX",
"types": ["country", "political"]
},
{
"long_name": "06350",
"short_name": "06350",
"types": ["postal_code"]
}
],
// Tipos de este lugar (puede ser múltiples)
"types": ["library", "point_of_interest", "establishment"]
}
],
// Estado de la respuesta
"status": "OK" // OK | ZERO_RESULTS | OVER_QUERY_LIMIT | REQUEST_DENIED | INVALID_REQUEST
}
📋 Códigos de Estado (status) Posibles:
| Status | Significado | Qué hacer |
|---|---|---|
OK |
Geocodificación exitosa | ✅ Procesar resultados normalmente |
ZERO_RESULTS |
No se encontró la dirección | ⚠️ Verificar que la dirección sea válida y específica |
OVER_QUERY_LIMIT |
Superaste el límite de peticiones | 🛑 Esperar o habilitar facturación en Google Cloud |
REQUEST_DENIED |
API Key inválida o API no habilitada | 🔑 Verificar API Key y habilitar Geocoding API |
INVALID_REQUEST |
Falta el parámetro "address" | 🐛 Revisar código: asegurar que se envíe la dirección |
UNKNOWN_ERROR |
Error del servidor (temporal) | 🔄 Reintentar la petición después de unos segundos |
🔍 Tipos de Precisión de Ubicación (location_type):
- ROOFTOP: Precisión más alta (±10 metros)
- Ubicación exacta del edificio
- Ideal para direcciones con número de calle
- Ejemplo: "Av. Universidad 3000, CDMX"
- RANGE_INTERPOLATED: Interpolación (±50 metros)
- Calculado entre dos puntos conocidos
- Usado cuando el número exacto no está en Google Maps
- Ejemplo: "Calle Falsa 123" (si no existe #123, calcula entre #120 y #130)
- GEOMETRIC_CENTER: Centro geométrico (±100 metros)
- Centro de una calle o zona
- Usado cuando solo se proporciona nombre de calle
- Ejemplo: "Av. Reforma, CDMX"
- APPROXIMATE: Aproximado (±1 km o más)
- Ubicación general de ciudad/región
- Usado para búsquedas muy genéricas
- Ejemplo: "Ciudad de México"
💻 ¿Cómo Usamos esta Información en Django?
def geocodificar_direccion(direccion):
# Llamar a Google Maps API
resultado = gmaps.geocode(direccion)
# Verificar que haya resultados
if resultado and len(resultado) > 0:
# Tomar el primer resultado (más relevante)
primer_resultado = resultado[0]
# Extraer coordenadas
location = primer_resultado['geometry']['location']
latitud = location['lat'] # 19.4395418
longitud = location['lng'] # -99.1520794
# Extraer Place ID (identificador único)
place_id = primer_resultado['place_id'] # "ChIJ..."
# Extraer dirección formateada
direccion_formateada = primer_resultado['formatted_address']
# Extraer tipo de precisión
location_type = primer_resultado['geometry']['location_type']
# Retornar diccionario con datos útiles
return {
'lat': Decimal(str(latitud)),
'lng': Decimal(str(longitud)),
'place_id': place_id,
'formatted_address': direccion_formateada,
'location_type': location_type
}
return None # No se encontró
3. 🗺️ Google Maps API - Configuración
🚨 ¡ADVERTENCIA CRÍTICA!
SI NO COMPLETAS ESTE PASO 1 CORRECTAMENTE
❌ LAS COORDENADAS NO APARECERÁN ❌
📋 CHECKLIST ANTES DE CONTINUAR:
| ☐ | Obtener API Key (siguiendo los pasos de abajo) |
| ☐ | Habilitar Geocoding API (en Google Cloud Console) |
| ☐ | Configurar Facturación ($200 crédito gratis) |
| ☐ | Guardar API Key en settings.py (al final del archivo) |
❌ Error que verás si NO haces esto:
ValueError: Must provide API key or enterprise credentials when creating client.
O también:
Error geocodificando: Must provide API key or enterprise credentials when creating client.
Y los campos quedarán vacíos:
- Latitud: -
- Longitud: -
- Place id: (vacío)
- Google maps url: (vacío)
✅ Sigue TODOS los pasos de abajo SIN SALTARTE NINGUNO
Paso 1: Obtener API Key (Este Paso 1, que contine 4 subpuntos pueden saltarlo, solo comenten como obtuvieron la API y porque fue Asi. En la Grabacion de su Video.
Ve a console.cloud.google.com y crea un nuevo proyecto
- Maps JavaScript API
- Geocoding API
- Places API
- Directions API
En "Credenciales", clic en "Crear credenciales" → "Clave de API"
⚠️ Seguridad de la API Key:
- Nunca subas tu API Key a GitHub
- Restringe la key por dominio o IP
- Limita las APIs que puede usar
- Usa variables de entorno
⚠️ Google Maps API requiere facturación activa, aunque ofrece $200 USD gratis al mes.
- En el menú lateral, haz clic en "Facturación"
- Haz clic en "Vincular una cuenta de facturación"
- Sigue los pasos para agregar una tarjeta de crédito/débito
- Confirma la cuenta de facturación
📝 Nota: No te cobrarán si no superas los $200/mes. Para estudiantes y proyectos pequeños, esto es más que suficiente.
🔍 Verificar que todo esté configurado:
- Ve a "APIs y servicios" → "Panel"
- Deberías ver las APIs habilitadas: Geocoding API, Maps JavaScript API
- Ve a "Facturación" → "Resumen"
- Deberías ver: "$200.00 de crédito gratis restante"
4. 💻 Saber Hacer - Implementación con Django
Paso 1: Instalar Dependencias
# Activar entorno virtual
.\venv\Scripts\Activate
# Instalar googlemaps para Python
pip install googlemaps==4.10.0 # Cliente oficial de Google Maps
# Instalar geopy para geocodificación
pip install geopy==2.4.0 # Biblioteca de geocodificación
# Actualizar requirements.txt
pip freeze > requirements.txt # Guarda todas las dependencias
Paso 2: Configurar Django
⚠️ IMPORTANTE - Este es el paso CRÍTICO
Aquí es donde configuras la API Key en Django.
Si NO haces este paso correctamente, tendrás el error:
ValueError: Must provide API key or enterprise credentials when creating client.
Ubicación: BibliotecasApp/biblioteca_project/settings.py
# Si estás en la carpeta BibliotecasApp
code biblioteca_project/settings.py
⚠️ AL FINAL significa después de todas las configuraciones existentes.
Busca las últimas líneas del archivo y agrega DESPUÉS de ellas.
# ================================
# GOOGLE MAPS API CONFIGURATION
# ================================
GOOGLE_MAPS_API_KEY = 'TU_API_KEY_AQUI' # ⭐ Reemplaza con tu API Key real
❌ NO DEJES: 'TU_API_KEY_AQUI'
✅ REEMPLÁZALA con tu API Key real, ejemplo:
GOOGLE_MAPS_API_KEY = 'AIzaSyBxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Checklist de verificación:
| ✓ | Las comillas están bien escritas (simples ' o dobles ") |
| ✓ | No hay espacios antes o después de las comillas |
| ✓ | Tu API Key empieza con AIza |
| ✓ | No tiene saltos de línea en medio |
| ✓ | Es tu API Key REAL (no el ejemplo TU_API_KEY_AQUI) |
Presiona CTRL + S (Windows) o CMD + S (Mac)
✅ VERIFICACIÓN INMEDIATA - Prueba que funciona
Antes de continuar, verifica que la API Key esté configurada correctamente:
python manage.py shell
from django.conf import settings
print("API Key configurada:", hasattr(settings, 'GOOGLE_MAPS_API_KEY'))
print("Primeros 10 caracteres:", settings.GOOGLE_MAPS_API_KEY[:10])
# Para salir:
exit()
✅ Si ves:
API Key configurada: True
Primeros 10 caracteres: AIzaSyBxxx
¡PERFECTO! Puedes continuar al Paso 3.
❌ Si ves un error o False:
- Verifica que guardaste el archivo settings.py (CTRL+S)
- Verifica que agregaste la línea AL FINAL del archivo
- Verifica que no hay errores de sintaxis (comillas, espacios)
- Cierra y vuelve a abrir Django shell
# Si quieres mayor seguridad, usa variables de entorno:
import os
GOOGLE_MAPS_API_KEY = os.environ.get('GOOGLE_MAPS_API_KEY', '') # Lee de variable de entorno
# O usando python-decouple:
from decouple import config
GOOGLE_MAPS_API_KEY = config('GOOGLE_MAPS_API_KEY') # Lee de archivo .env
Paso 3: Crear Modelos
from django.db import models
from decimal import Decimal
class Biblioteca(models.Model):
"""Modelo para bibliotecas con ubicación geográfica"""
nombre = models.CharField(max_length=200) # Nombre de la biblioteca
direccion = models.CharField(max_length=300) # Dirección postal
ciudad = models.CharField(max_length=100) # Ciudad
pais = models.CharField(max_length=100, default='México') # País
# Coordenadas geográficas (se llenan automáticamente)
latitud = models.DecimalField(
max_digits=10,
decimal_places=7,
null=True,
blank=True
) # Latitud con 7 decimales de precisión
longitud = models.DecimalField(
max_digits=10,
decimal_places=7,
null=True,
blank=True
) # Longitud con 7 decimales de precisión
# Datos adicionales de Google Maps
place_id = models.CharField(max_length=200, blank=True) # ID único de Google
google_maps_url = models.URLField(blank=True) # URL de Google Maps
telefono = models.CharField(max_length=20, blank=True) # Teléfono
horario = models.TextField(blank=True) # Horario de atención
class Meta:
verbose_name = 'Biblioteca'
verbose_name_plural = 'Bibliotecas'
ordering = ['nombre']
def __str__(self):
return self.nombre
@property
def direccion_completa(self):
"""Retorna dirección formateada"""
return f"{self.direccion}, {self.ciudad}, {self.pais}" # Dirección completa
def generar_google_maps_url(self):
"""
Genera URL de Google Maps basada en las coordenadas
Formatos disponibles:
1. Con Place ID (más preciso): https://www.google.com/maps/place/?q=place_id:ChIJ...
2. Con coordenadas: https://www.google.com/maps?q=lat,lng
3. Con búsqueda: https://www.google.com/maps/search/?api=1&query=nombre+direccion
"""
if self.place_id:
# Opción 1: Usar Place ID (más preciso)
return f"https://www.google.com/maps/place/?q=place_id:{self.place_id}"
elif self.latitud and self.longitud:
# Opción 2: Usar coordenadas
return f"https://www.google.com/maps?q={self.latitud},{self.longitud}"
else:
# Opción 3: Búsqueda por texto
import urllib.parse
query = urllib.parse.quote(self.direccion_completa)
return f"https://www.google.com/maps/search/?api=1&query={query}"
def geocodificar(self):
"""Obtiene coordenadas de la dirección"""
from .services import GoogleMapsService
resultado = GoogleMapsService.geocodificar_direccion(self.direccion_completa)
if resultado:
self.latitud = resultado['lat'] # Guarda latitud
self.longitud = resultado['lng'] # Guarda longitud
self.place_id = resultado.get('place_id', '') # Guarda Place ID
self.google_maps_url = self.generar_google_maps_url() # ⭐ Genera URL automáticamente
self.save() # Guarda en la base de datos
return True
return False
Paso 4: Crear Servicio de Google Maps
import googlemaps
from django.conf import settings
from decimal import Decimal
import logging
logger = logging.getLogger(__name__) # Logger para errores
class GoogleMapsService:
"""Servicio para interactuar con Google Maps API"""
def __init__(self):
self.client = googlemaps.Client(key=settings.GOOGLE_MAPS_API_KEY) # Cliente de Google Maps
@classmethod
def geocodificar_direccion(cls, direccion):
"""
Convierte dirección en coordenadas
Args:
direccion (str): Dirección completa
Returns:
dict: {'lat': float, 'lng': float, 'place_id': str}
"""
try:
service = cls() # Instancia del servicio
resultado = service.client.geocode(direccion) # Llama a Geocoding API
if resultado: # Si encuentra resultados
location = resultado[0]['geometry']['location'] # Obtiene ubicación
place_id = resultado[0]['place_id'] # ID del lugar
return {
'lat': Decimal(str(location['lat'])), # Convierte a Decimal
'lng': Decimal(str(location['lng'])), # Convierte a Decimal
'place_id': place_id,
'formatted_address': resultado[0]['formatted_address'] # Dirección formateada
}
return None # No se encontró
except Exception as e:
logger.error(f"Error geocodificando: {e}") # Registra error
return None
@classmethod
def calcular_distancia(cls, origen, destino):
"""
Calcula distancia entre dos puntos
Args:
origen (tuple): (lat, lng) del origen
destino (tuple): (lat, lng) del destino
Returns:
dict: Información de distancia y duración
"""
try:
service = cls()
resultado = service.client.distance_matrix(
origins=[origen], # Punto de inicio
destinations=[destino], # Punto de destino
mode='driving', # Modo: driving, walking, bicycling, transit
language='es' # Idioma español
)
if resultado['rows']:
element = resultado['rows'][0]['elements'][0]
if element['status'] == 'OK':
return {
'distancia_texto': element['distance']['text'], # "15.2 km"
'distancia_metros': element['distance']['value'], # 15200
'duracion_texto': element['duration']['text'], # "25 mins"
'duracion_segundos': element['duration']['value'] # 1500
}
return None
except Exception as e:
logger.error(f"Error calculando distancia: {e}")
return None
5. 🚀 Proyecto Práctico: Sistema de Bibliotecas
Objetivo del Proyecto
Crear un sistema que permita registrar bibliotecas con su ubicación geográfica, mostrarlas en un mapa interactivo y calcular distancias entre ellas.
🎓 Práctica Guiada Paso a Paso (Para Principiantes)
📝 Antes de Empezar
¿Qué necesitas tener instalado en tu computadora?
- ✅ Python 3.8 o superior - Descarga de python.org
- ✅ MySQL Server - Descarga de mysql.com
- ✅ Visual Studio Code o cualquier editor de código
- ✅ Un navegador web moderno (Chrome, Firefox, Edge)
- ✅ Cuenta de Google para obtener API Key
✅ ¡EMPIEZA AQUÍ LA PRÁCTICA!
Si completaste la sección de Requisitos Previos, ahora sí puedes empezar.
Sigue estos 7 PASOS EN ORDEN:
- PASO 1: Obtener API Key de Google Maps (⬇️ EMPIEZA ABAJO)
- PASO 2: Crear Proyecto Django
- PASO 3: Configurar MySQL
- PASO 4: Escribir el Código
- PASO 5: Panel de Administración
- PASO 6: Crear API REST
- PASO 7: Mapa Interactivo
⚠️ NO te saltes pasos - cada uno es necesario para el siguiente.
📋 PASO 1: Obtener tu API Key de Google Maps (15 minutos)
🎯 Objetivo de este paso:
Obtener una API Key (clave de API) de Google Maps que nos permitirá:
- ✅ Convertir direcciones en coordenadas GPS (geocodificación)
- ✅ Mostrar mapas interactivos en nuestra aplicación
- ✅ Usar los servicios de Google Maps gratis (hasta $200 USD/mes)
⏱️ Tiempo estimado: 15 minutos
📸 Toma captura: De tu API Key cuando la obtengas (la necesitarás después)
- Abre tu navegador y ve a: https://console.cloud.google.com
- Inicia sesión con tu cuenta de Gmail
- En la parte superior, haz clic en "Seleccionar proyecto"
- Haz clic en "Proyecto nuevo"
- Escribe un nombre para tu proyecto:
Bibliotecas-Geolocalización - Haz clic en "Crear" y espera unos segundos
- En el menú lateral izquierdo, haz clic en "APIs y servicios" → "Biblioteca"
- Busca "Maps JavaScript API" y haz clic en ella
- Haz clic en el botón azul "HABILITAR"
- Regresa a la Biblioteca y busca "Geocoding API"
- Haz clic en "HABILITAR"
- Repite el proceso para "Places API" y "Directions API"
- En el menú lateral, haz clic en "APIs y servicios" → "Credenciales"
- Haz clic en "+ CREAR CREDENCIALES" en la parte superior
- Selecciona "Clave de API"
- Se generará una clave (algo como:
AIzaSyD1234...) - ¡MUY IMPORTANTE! Copia esta clave y guárdala en un lugar seguro (Notepad)
- Haz clic en "RESTRINGIR CLAVE" (para seguridad)
- En "Restricciones de API", selecciona las APIs que habilitaste
- Haz clic en "Guardar"
⚠️ ¡Atención!
¿Por qué necesitas una API Key?
La API Key es como una "contraseña" que le dice a Google: "Soy yo, permíteme usar tus mapas". Sin ella, no podrás usar Google Maps en tu proyecto.
⚠️ Nunca compartas tu API Key públicamente (no la subas a GitHub, no la pegues en redes sociales)
📋 PASO 2: Configurar tu Proyecto Django (20 minutos)
- Abre la terminal de Windows (Windows PowerShell) presionando
Windows + R, escribepowershelly presiona Enter - Navega a donde quieres guardar el proyecto. Ejemplo:
# Ir a tu carpeta de Documentos
cd ~\Documents
# Crear carpeta para el proyecto
mkdir BibliotecasApp
# Entrar a la carpeta
cd BibliotecasApp
¿Qué es un entorno virtual? Es como una "caja" donde instalamos las bibliotecas de Python solo para este proyecto, sin afectar otras aplicaciones.
# Crear entorno virtual llamado "venv"
python -m venv venv
# Activar el entorno virtual
.\venv\Scripts\Activate
# Si sale un error de permisos en PowerShell, ejecuta:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
# Luego intenta activar nuevamente
.\venv\Scripts\Activate
# Deberías ver (venv) al inicio de la línea, significa que está activo
# Actualizar pip primero (IMPORTANTE)
python -m pip install --upgrade pip
# Instalar Django
pip install django==4.2
# Instalar Django REST Framework (para crear APIs)
pip install djangorestframework
# Instalar cliente de Google Maps
pip install googlemaps==4.10.0
# Instalar geopy (para geocodificación)
pip install geopy==2.4.0
# Verificar que se instaló todo correctamente
pip list
⚠️ Instalación de mysqlclient en Windows
La instalación de mysqlclient puede fallar en Windows. Sigue estos pasos:
Opción 1 - Usando archivo wheel (RECOMENDADO):
- Descarga el archivo wheel desde: https://www.lfd.uci.edu/~gohlke/pythonlibs/#mysqlclient
- Selecciona la versión según tu Python:
- Python 3.11 64-bit:
mysqlclient-2.2.0-cp311-cp311-win_amd64.whl - Python 3.10 64-bit:
mysqlclient-2.2.0-cp310-cp310-win_amd64.whl - Python 3.9 64-bit:
mysqlclient-2.2.0-cp39-cp39-win_amd64.whl
- Python 3.11 64-bit:
- Guarda el archivo en la carpeta
BibliotecasApp - Instálalo con:
pip install nombredelarchivo.whl
Opción 2 - Instalar Visual Studio Build Tools:
- Descarga: Visual C++ Build Tools
- Instala con la opción "Desarrollo de escritorio con C++"
- Reinicia la terminal
- Ejecuta:
pip install mysqlclient
Opción 3 - Usar PyMySQL (alternativa más fácil):
# En lugar de mysqlclient, instala pymysql
pip install pymysql
# Luego, en settings.py agrega al inicio:
# import pymysql
# pymysql.install_as_MySQLdb()
¿Qué hace cada biblioteca?
django- El framework web para crear nuestro backendmysqlclient/pymysql- Permite que Django se conecte a MySQLdjangorestframework- Herramienta para crear APIs REST fácilmentegooglemaps- Cliente oficial de Google Maps para Pythongeopy- Biblioteca para trabajar con coordenadas geográficas
# Crear proyecto Django llamado "biblioteca_project"
django-admin startproject biblioteca_project .
# El punto (.) al final significa "crear en la carpeta actual"
# Crear aplicación llamada "libros"
python manage.py startapp libros
# Verificar estructura creada
dir
📁 Estructura de carpetas que deberías ver:
BibliotecasApp/
│
├── venv/ # Entorno virtual
├── biblioteca_project/ # Configuración del proyecto
│ ├── __init__.py
│ ├── settings.py # ⭐ Configuraciones principales
│ ├── urls.py # ⭐ Rutas/URLs del proyecto
│ └── wsgi.py
├── libros/ # Nuestra aplicación
│ ├── migrations/
│ ├── __init__.py
│ ├── admin.py # ⭐ Panel de administración
│ ├── models.py # ⭐ Modelos de base de datos
│ ├── views.py # ⭐ Vistas/lógica
│ └── tests.py
└── manage.py # ⭐ Comando principal de Django
📋 PASO 3: Configurar Base de Datos MySQL (15 minutos)
- Abre MySQL Workbench o phpMyAdmin
- Si usas línea de comandos, abre una nueva terminal y ejecuta:
-- Conectarse a MySQL (te pedirá tu contraseña)
mysql -u root -p
-- Crear base de datos
CREATE DATABASE biblioteca_db CHARACTER SET utf8mb4;
-- Crear usuario para la aplicación (recomendado por seguridad)
CREATE USER 'biblioteca_user'@'localhost' IDENTIFIED BY 'password123';
-- Dar permisos al usuario sobre la base de datos
GRANT ALL PRIVILEGES ON biblioteca_db.* TO 'biblioteca_user'@'localhost';
-- Aplicar cambios
FLUSH PRIVILEGES;
-- Salir de MySQL
EXIT;
Abre el archivo biblioteca_project/settings.py en tu editor de código:
📝 Si usaste PyMySQL (Opción 3)
Agrega estas líneas al inicio del archivo settings.py, después de los imports:
import pymysql
pymysql.install_as_MySQLdb() # Hace que Django use PyMySQL como si fuera mysqlclient
Ahora busca la sección DATABASES (alrededor de la línea 75) y reemplázala:
# Busca esta sección y reemplázala:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # Motor MySQL
'NAME': 'biblioteca_db', # Nombre de la base de datos
'USER': 'biblioteca_user', # Usuario de MySQL
'PASSWORD': 'password123', # Contraseña del usuario
'HOST': 'localhost', # Servidor (local)
'PORT': '3306', # Puerto por defecto de MySQL
'OPTIONS': {
'charset': 'utf8mb4', # Soporte para emojis y caracteres especiales
}
}
}
# Más arriba en el archivo, busca INSTALLED_APPS y agrégale:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework', # ⭐ Agregar Django REST Framework
'libros', # ⭐ Agregar nuestra aplicación
]
# Al final del archivo, agregar tu API Key de Google Maps
GOOGLE_MAPS_API_KEY = 'AIzaSyD1234...' # ⭐ Pega tu API Key aquí (la que copiaste en el PASO 1)
# Configuración de idioma español
LANGUAGE_CODE = 'es-mx'
TIME_ZONE = 'America/Mexico_City' # Zona horaria de México
⚠️ Verificar antes de continuar:
- ✅ Reemplazaste
'biblioteca_db'con el nombre de tu base de datos - ✅ Reemplazaste
'biblioteca_user'con tu usuario de MySQL - ✅ Reemplazaste
'password123'con tu contraseña de MySQL - ✅ Agregaste
'rest_framework'y'libros'en INSTALLED_APPS - ✅ Pegaste tu API Key de Google Maps (sin las comillas)
💡 Consejo de Seguridad
Nunca subas tu API Key a GitHub. En proyectos reales, usa variables de entorno:
- Crea un archivo
.enven la raíz del proyecto - Instala
python-decouple:pip install python-decouple - En
.env:GOOGLE_MAPS_API_KEY=tu_clave_aqui - En
settings.py:from decouple import configyGOOGLE_MAPS_API_KEY = config('GOOGLE_MAPS_API_KEY') - Agrega
.enva tu archivo.gitignore
📋 PASO 4: Crear los Modelos (15 minutos)
Un modelo en Django es una clase de Python que representa una tabla en la base de datos. Cada atributo del modelo es un campo (columna) en la tabla.
Ejemplo: Si queremos guardar bibliotecas, necesitamos campos como: nombre, dirección, ciudad, latitud, longitud, etc.
Primero, crea un archivo nuevo libros/services.py (haz clic derecho en la carpeta libros → Nuevo archivo → services.py)
Este archivo contendrá el código para comunicarse con Google Maps API. Copia el código completo de la Sección 4, Paso 4 de esta guía.
Abre el archivo libros/models.py y copia el código completo de la Sección 4, Paso 3 de esta guía.
# Crear archivos de migración (Django analiza tus modelos)
python manage.py makemigrations
# Deberías ver un mensaje como: "Migrations for 'libros': libros/migrations/0001_initial.py"
# Aplicar las migraciones (crear tablas en MySQL)
python manage.py migrate
# Deberías ver varios "OK" confirmando que se crearon las tablas
🎉 ¡Felicidades! Ya tienes la tabla libros_biblioteca creada en MySQL con todos los campos.
🚨 Si aparece un error al ejecutar migrate:
Error común: "Access denied for user"
- Verifica que MySQL esté corriendo (abre MySQL Workbench o XAMPP)
- Verifica usuario y contraseña en
settings.py - Asegúrate de haber creado la base de datos
biblioteca_db
Error: "No module named 'MySQLdb'"
- Si usas PyMySQL, verifica que agregaste las líneas al inicio de
settings.py - Si usas mysqlclient, reinstala:
pip uninstall mysqlclienty luegopip install mysqlclient
Verificar que la tabla se creó:
-- Conectarse a la base de datos
USE biblioteca_db;
-- Ver todas las tablas
SHOW TABLES;
-- Deberías ver: libros_biblioteca
-- Ver estructura de la tabla
DESCRIBE libros_biblioteca;
📋 PASO 5: Configurar Panel de Administración (10 minutos)
Abre libros/admin.py y agrega:
from django.contrib import admin
from django.utils.html import format_html
from .models import Biblioteca
@admin.register(Biblioteca)
class BibliotecaAdmin(admin.ModelAdmin):
"""Panel de administración para Bibliotecas"""
# Campos a mostrar en la lista
list_display = ['nombre', 'ciudad', 'pais', 'latitud', 'longitud', 'ver_en_mapa']
# Filtros en la barra lateral
list_filter = ['ciudad', 'pais']
# Campos de búsqueda
search_fields = ['nombre', 'direccion', 'ciudad']
# Campos solo de lectura (no editables)
readonly_fields = ['latitud', 'longitud', 'place_id', 'google_maps_url', 'ver_en_mapa']
# Acción personalizada para geocodificar
actions = ['geocodificar_seleccionadas']
def ver_en_mapa(self, obj):
"""Muestra un enlace para abrir en Google Maps"""
if obj.google_maps_url:
return format_html(
'<a href="{}" target="_blank" style="color: #11998e; font-weight: bold;">🗺️ Abrir en Google Maps</a>',
obj.google_maps_url
)
return '-'
ver_en_mapa.short_description = 'Ver en Mapa'
def geocodificar_seleccionadas(self, request, queryset):
"""Geocodifica las bibliotecas seleccionadas"""
contador = 0
for biblioteca in queryset:
if biblioteca.geocodificar():
contador += 1
self.message_user(request, f'{contador} bibliotecas geocodificadas exitosamente.')
geocodificar_seleccionadas.short_description = "Geocodificar bibliotecas seleccionadas"
# Crear superusuario (administrador)
python manage.py createsuperuser
# Te pedirá:
# Username: (escribe: admin)
# Email: (escribe tu correo o déjalo en blanco)
# Password: (escribe una contraseña segura, no se verá al escribir)
# Password (again): (repite la misma contraseña)
# Iniciar servidor de desarrollo
python manage.py runserver
# Deberías ver algo como:
# Starting development server at http://127.0.0.1:8000/
# Quit the server with CTRL-BREAK.
✅ Paso a Paso - Probar el Admin:
- Abre tu navegador (Chrome, Firefox, Edge)
- Ve a:
http://127.0.0.1:8000/admin/ - Deberías ver la página de login de Django (fondo azul)
- Ingresa:
- Usuario:
admin(el que creaste) - Contraseña: (la que pusiste)
- Usuario:
- Haz clic en "Iniciar sesión"
🎯 Si todo salió bien, deberías ver:
- ✅ Panel de administración de Django
- ✅ Sección "AUTENTICACIÓN Y AUTORIZACIÓN" (Usuarios, Grupos)
- ✅ Sección "LIBROS" con opción "Bibliotecas" ← ¡ESTO ES LO IMPORTANTE!
Ahora vamos a crear una biblioteca de prueba:
- Haz clic en "Bibliotecas"
- Haz clic en "Agregar Biblioteca +" (botón verde arriba a la derecha)
- Llena el formulario:
Datos de Ejemplo
Nombre: Biblioteca Central (UNAM) Dirección: Circuito Interior s/n, Ciudad Universitaria, C.P. 04510 Ciudad: Ciudad de México País: México Teléfono: +504 2222-0000 Horario: Lunes a Viernes: 8:00 AM - 6:00 PM - Haz clic en "Guardar" (botón azul abajo)
- ⭐ IMPORTANTE: Los campos latitud, longitud y place_id aparecerán vacíos por ahora. Esto es normal.
Ahora geocodifiquemos la biblioteca:
- En la lista de bibliotecas, marca la casilla junto a "Biblioteca Central (UNAM)a"
- En el menú desplegable "Acción" (arriba), selecciona "Geocodificar bibliotecas seleccionadas"
- Haz clic en el botón "Ir"
- Deberías ver un mensaje verde: "1 bibliotecas geocodificadas exitosamente."
- Haz clic en la biblioteca para abrirla nuevamente
- ¡Ahora deberías ver las coordenadas!
- Latitud:
14.0723(aproximadamente) - Longitud:
-87.1921(aproximadamente) - Place ID:
ChIJ... - Google Maps URL:
https://www.google.com/maps/...
- Latitud:
- Haz clic en el enlace "🗺️ Abrir en Google Maps" para verificar que funciona
✅ ¿Cómo se VE cuando SÍ funciona correctamente?
Cuando la geocodificación funciona, los campos se llenan AUTOMÁTICAMENTE así:
📍 Ejemplo: Biblioteca Vasconcelos
| Campo | Valor (aparece automáticamente) |
|---|---|
| Nombre: | Biblioteca Vasconcelos |
| Dirección: | Eje 1 Norte Mosqueta S/N |
| Ciudad: | Ciudad de México |
| País: | México |
| Latitud: ⭐ | 19.4395418 |
| Longitud: ⭐ | -99.1520794 |
| Place ID: ⭐ | ChIJXxQBnbP_0YURW9JOEKiHlBM |
| Google Maps URL: ⭐ | https://www.google.com/maps/place/?q=place_id:ChIJXxQBnbP_0YURW9JOEKiHlBM |
⭐ Los campos marcados con estrella se llenan AUTOMÁTICAMENTE al guardar.
🎯 Más Ejemplos de Coordenadas Correctas - Bibliotecas de México
| Biblioteca | Latitud | Longitud | Ciudad |
|---|---|---|---|
| Biblioteca Nacional de México | 19.3316 | -99.1867 | Ciudad de México |
| Biblioteca Central UNAM | 19.3317 | -99.1903 | Ciudad de México |
| Biblioteca de México José Vasconcelos | 19.4395 | -99.1521 | Ciudad de México |
| Biblioteca Iberoamericana Octavio Paz | 19.3773 | -99.2636 | Ciudad de México |
| Biblioteca Palafoxiana | 19.0414 | -98.2063 | Puebla |
| Biblioteca Pública del Estado de Jalisco | 20.6767 | -103.3475 | Guadalajara |
| Biblioteca Universitaria Alfonso Reyes | 25.7260 | -100.3115 | Monterrey |
💡 Nota: Estas son coordenadas reales de bibliotecas importantes en México. Puedes usarlas para probar tu aplicación.
📸 Muestra en Pantalla
Cuando tus coordenadas aparezcan correctamente:
- Muestra la página del admin mostrando los campos llenos
- Haz clic en "🗺️ Abrir en Google Maps" y muestra el mapa
🚨 ¡PROBLEMA MÁS COMÚN! - Coordenadas No Aparecen
Si después de crear una biblioteca los campos quedan vacíos:
❌ Latitud: -
❌ Longitud: -
❌ Place id: (vacío)
❌ Google maps url: (vacío)
🔍 CAUSA #1: API Key No Configurada o Incorrecta (90% de los casos)
# Busca al FINAL del archivo settings.py
# Debe existir esta línea:
GOOGLE_MAPS_API_KEY = 'AIzaSyD1234567890abcdefghijklmnopqrstuvwxyz' # ⭐ TU CLAVE AQUÍ
# ⚠️ VERIFICA:
# ✅ Que esté entre comillas simples o dobles
# ✅ Que sea TU API Key (no el ejemplo)
# ✅ Que no tenga espacios al inicio o final
# ✅ Que empiece con 'AIza'
Si no existe esa línea: Agrégala AL FINAL del archivo settings.py y reinicia el servidor.
🔍 CAUSA #2: Geocoding API No Habilitada
- Ve a: Google Cloud Console - APIs
- Selecciona tu proyecto
Bibliotecas-Geolocalización - Verifica que aparezcan estas 2 APIs HABILITADAS:
- ✅ Geocoding API
- ✅ Maps JavaScript API
- Si NO están habilitadas:
- Clic en "Biblioteca" (menú izquierdo)
- Busca "Geocoding API"
- Clic en "HABILITAR"
- Espera 2-3 minutos
🔍 CAUSA #3: Facturación No Configurada
- Ve a: Google Cloud Console - Facturación
- Verifica que tu proyecto tenga una cuenta de facturación vinculada
- Debes ver: "$200.00 de crédito gratis"
- Si NO está vinculada:
- Clic en "Vincular una cuenta de facturación"
- Sigue el proceso (necesitas tarjeta, pero NO te cobrarán si no superas $200/mes)
🔍 CAUSA #4: Servidor Django No Reiniciado
# En la terminal donde corre el servidor:
# 1. Presiona CTRL + C para detenerlo
# 2. Inicia nuevamente:
python manage.py runserver
# 3. Espera a que diga "Starting development server..."
# 4. Intenta crear una biblioteca nuevamente
🔍 CAUSA #5: Error en el Código de Geocodificación
# Abre Django shell
python manage.py shell
# Dentro del shell, ejecuta:
from libros.services import GoogleMapsService
# Probar geocodificación
resultado = GoogleMapsService.geocodificar_direccion("Biblioteca Vasconcelos, Ciudad de México, México")
print(resultado)
# Si funciona, deberías ver:
# {'lat': Decimal('19.4395'), 'lng': Decimal('-99.1520'), 'place_id': 'ChIJ...', ...}
# Si NO funciona, verás:
# None
# o un error con el mensaje específico
# Para salir del shell:
exit()
Si ves None o un error:
- El error te dirá qué está mal (API Key, API no habilitada, facturación, etc.)
- Consulta el archivo
SOLUCION_COORDENADAS_NO_APARECEN.mdpara más detalles
⚠️ ERRORES MÁS COMUNES al Probar en Django Shell
❌ ERROR #1: "Must provide API key"
ValueError: Must provide API key or enterprise credentials when creating client.
🔧 SOLUCIÓN:
- Abre
biblioteca_project/settings.py - Verifica que AL FINAL del archivo exista:
settings.py (al final)
GOOGLE_MAPS_API_KEY = 'AIzaSyD1234567890abcdefghijklmnopqrstuvwxyz' - Si NO existe: Agrégala con TU API Key (la que copiaste en el PASO 1)
- Si SÍ existe: Verifica que:
- ✓ No tenga espacios antes o después de las comillas
- ✓ Las comillas estén bien escritas (simples
'o dobles") - ✓ Sea tu API Key real (no el ejemplo)
- ✓ Empiece con
AIza
- GUARDA el archivo settings.py
- SALE del Django shell:
exit() - REINICIA el servidor:
PowerShell
# Detén el servidor con CTRL + C python manage.py runserver - VUELVE A INTENTAR en el Django shell
❌ ERROR #2: "This API project is not authorized"
googlemaps.exceptions.ApiError: This API project is not authorized to use this API.
🔧 SOLUCIÓN:
- Ve a: Habilitar Geocoding API
- Selecciona tu proyecto
Bibliotecas-Geolocalización - Clic en "HABILITAR"
- Espera 2-3 minutos
- Vuelve a intentar en Django shell
❌ ERROR #3: "You must enable Billing"
googlemaps.exceptions.ApiError: You must enable Billing on the Google Cloud Project
🔧 SOLUCIÓN:
- Ve a: Google Cloud - Facturación
- Vincula una cuenta de facturación (necesitas tarjeta)
- Obtienes $200 USD de crédito gratis
- NO te cobrarán si no superas ese límite
❌ ERROR #4: "ZERO_RESULTS"
Si recibes None sin error, significa que Google no encontró la dirección.
🔧 SOLUCIÓN:
- Verifica que la dirección sea completa y correcta
- Incluye ciudad y país siempre:
"Biblioteca Vasconcelos, Ciudad de México, México" - Prueba con una dirección conocida (usa los ejemplos de la tabla arriba)
💡 PRUEBA RÁPIDA - Copia y Pega
Para verificar que todo funciona, copia EXACTAMENTE este código en el Django shell:
from django.conf import settings
import googlemaps
# Ver si la API Key está configurada
print("API Key configurada:", hasattr(settings, 'GOOGLE_MAPS_API_KEY'))
# Si dice True, continúa:
print("API Key (primeros 10 caracteres):", settings.GOOGLE_MAPS_API_KEY[:10])
# Probar conexión
gmaps = googlemaps.Client(key=settings.GOOGLE_MAPS_API_KEY)
resultado = gmaps.geocode("Biblioteca Vasconcelos, Ciudad de México, México")
# Si funciona, verás un array con resultados
print("Latitud:", resultado[0]['geometry']['location']['lat'])
print("Longitud:", resultado[0]['geometry']['location']['lng'])
✅ Si ves latitud y longitud, TODO FUNCIONA CORRECTAMENTE
✅ SOLUCIÓN RÁPIDA - Checklist Completo
Verifica EN ORDEN:
| Listo | Verificación | Cómo verificar |
|---|---|---|
| [ ] | API Key en settings.py | Abre settings.py, busca GOOGLE_MAPS_API_KEY al final |
| [ ] | Geocoding API habilitada | Google Cloud Console → APIs → Geocoding API (debe estar ENABLED) |
| [ ] | Facturación activa | Google Cloud Console → Facturación (debe mostrar $200 crédito) |
| [ ] | Servidor reiniciado | Detener con CTRL+C y ejecutar python manage.py runserver |
| [ ] | Código correcto | Probar en python manage.py shell (comando arriba) |
| [ ] | Conexión a Internet | Google Maps API necesita Internet para funcionar |
📖 Documentación Completa de Soluciones
Para TODAS las causas posibles y soluciones detalladas, consulta el archivo:
📄 SOLUCION_COORDENADAS_NO_APARECEN.md
Este archivo contiene:
- ✅ 8 causas comunes de errores
- ✅ Soluciones paso a paso para cada una
- ✅ Comandos de diagnóstico
- ✅ Ejemplos de mensajes de error y qué significan
🚨 ¿No aparecen las coordenadas después de geocodificar?
Revisa estos puntos:
- Abre la consola de Django (donde corre
python manage.py runserver) - Busca mensajes de error como:
ERROR: Error geocodificando: Invalid API key→ API Key incorrectaThis API project is not authorized→ Geocoding API no habilitadaYou must enable Billing→ Facturación no configurada
- Consulta el archivo:
SOLUCION_COORDENADAS_NO_APARECEN.mdpara más ayuda
Prueba rápida de la API Key:
# Activar entorno virtual
.\venv\Scripts\Activate
# Abrir shell de Django
python manage.py shell
# Ejecutar en el shell:
>>> import googlemaps
>>> from django.conf import settings
>>> gmaps = googlemaps.Client(key=settings.GOOGLE_MAPS_API_KEY)
>>> resultado = gmaps.geocode('Ciudad de México, México')
>>> print(resultado)
# Si ves un error, tu API Key o configuración tiene problemas
# Si ves datos JSON, ¡funciona correctamente!
✅ Checkpoint: ¿Qué has logrado hasta ahora?
- ✅ Obtuviste tu API Key de Google Maps
- ✅ Creaste un proyecto Django desde cero
- ✅ Configuraste MySQL como base de datos
- ✅ Creaste un modelo para guardar bibliotecas con geolocalización
- ✅ Probaste la geocodificación automática en el admin
- ✅ Se genera automáticamente el enlace de Google Maps
🚀 Siguiente paso: Crear una API REST y el frontend con mapas interactivos.
🗺️ ¿Cómo se genera el Google Maps URL?
El método generar_google_maps_url() crea automáticamente un enlace a Google Maps usando 3 estrategias:
- Opción 1 - Place ID (más preciso):
https://www.google.com/maps/place/?q=place_id:ChIJ...Utiliza el identificador único de Google Maps. Es la opción más precisa.
- Opción 2 - Coordenadas:
https://www.google.com/maps?q=19.4474100,-99.1508100Usa latitud y longitud directamente. Funciona cuando no hay Place ID.
- Opción 3 - Búsqueda por texto:
https://www.google.com/maps/search/?api=1&query=dirección+ciudad+paísHace una búsqueda por texto. Se usa cuando no hay coordenadas.
Ejemplo de uso:
from libros.models import Biblioteca
# Obtener una biblioteca
biblioteca = Biblioteca.objects.get(id=1)
# Ver el URL generado
print(biblioteca.google_maps_url)
# Output: https://www.google.com/maps/place/?q=place_id:ChIJ...
# Abrir directamente en el navegador desde la terminal
import webbrowser
webbrowser.open(biblioteca.google_maps_url)
📝 Formatos de URL de Google Maps
| Formato | Cuando se Usa | Ejemplo |
|---|---|---|
| Place ID | Cuando Google devuelve un Place ID | ?q=place_id:ChIJ... |
| Coordenadas | Cuando tienes lat/lng pero no Place ID | ?q=14.0723,-87.1921 |
| Búsqueda | Cuando solo tienes dirección de texto | search/?api=1&query=... |
| Directions | Para rutas de A a B | dir/?api=1&origin=...&destination=... |
📋 PASO 6: Crear API REST (20 minutos)
🤔 ¿Qué es una API REST?
API = Forma en que dos programas se comunican
REST = Conjunto de reglas para crear APIs
En resumen: Es como un "menú de restaurante" que le dice al frontend (página web) qué datos puede pedir y cómo pedirlos.
Ejemplo: GET /api/bibliotecas/ → "Dame la lista de todas las bibliotecas"
Los serializers convierten objetos de Python en JSON (formato que entienden los navegadores).
Crea un archivo nuevo: libros/serializers.py
from rest_framework import serializers
from .models import Biblioteca
class BibliotecaSerializer(serializers.ModelSerializer):
"""
Serializer para convertir el modelo Biblioteca a JSON
¿Qué hace?
- Convierte objetos Python → JSON (para enviar al frontend)
- Convierte JSON → objetos Python (cuando recibe datos del frontend)
"""
# Campo adicional calculado automáticamente
direccion_completa = serializers.CharField(read_only=True)
class Meta:
model = Biblioteca # ¿Qué modelo serializar?
fields = '__all__' # Incluir todos los campos del modelo
# Campos que no se pueden editar (se calculan automáticamente)
read_only_fields = ['latitud', 'longitud', 'place_id']
def create(self, validated_data):
"""
Se ejecuta cuando se crea una nueva biblioteca
1. Crea la biblioteca en la base de datos
2. Llama a geocodificar() para obtener las coordenadas
3. Retorna la biblioteca con coordenadas
"""
biblioteca = super().create(validated_data) # Crear en BD
biblioteca.geocodificar() # Obtener coordenadas de Google Maps
return biblioteca
Los ViewSets manejan las peticiones HTTP (GET, POST, PUT, DELETE).
Crea un archivo nuevo: libros/api_views.py
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from .models import Biblioteca
from .serializers import BibliotecaSerializer
from .services import GoogleMapsService
class BibliotecaViewSet(viewsets.ModelViewSet):
"""
ViewSet que maneja todas las operaciones con Bibliotecas
Endpoints que crea automáticamente:
- GET /api/bibliotecas/ → Listar todas
- POST /api/bibliotecas/ → Crear nueva
- GET /api/bibliotecas/{id}/ → Ver una específica
- PUT /api/bibliotecas/{id}/ → Actualizar
- DELETE /api/bibliotecas/{id}/ → Eliminar
"""
queryset = Biblioteca.objects.all() # Todas las bibliotecas
serializer_class = BibliotecaSerializer # Usar este serializer
# Endpoint personalizado: /api/bibliotecas/cercanas/?lat=14.0723&lng=-87.1921
@action(detail=False, methods=['get'])
def cercanas(self, request):
"""
Obtiene bibliotecas cercanas a una ubicación
Parámetros requeridos:
- lat: Latitud del usuario
- lng: Longitud del usuario
Ejemplo: GET /api/bibliotecas/cercanas/?lat=14.0723&lng=-87.1921
"""
# Obtener parámetros de la URL
lat = request.query_params.get('lat')
lng = request.query_params.get('lng')
# Validar que vengan los parámetros
if not lat or not lng:
return Response(
{'error': 'Se requieren parámetros lat y lng'},
status=status.HTTP_400_BAD_REQUEST
)
# Filtrar solo bibliotecas con coordenadas
bibliotecas = self.queryset.filter(
latitud__isnull=False,
longitud__isnull=False
)
# Convertir a JSON y retornar
serializer = self.get_serializer(bibliotecas, many=True)
return Response(serializer.data)
# Endpoint personalizado: POST /api/bibliotecas/{id}/geocodificar/
@action(detail=True, methods=['post'])
def geocodificar(self, request, pk=None):
"""
Geocodifica una biblioteca específica manualmente
Ejemplo: POST /api/bibliotecas/1/geocodificar/
"""
biblioteca = self.get_object() # Obtener la biblioteca por ID
if biblioteca.geocodificar(): # Intentar geocodificar
return Response({
'mensaje': 'Geocodificación exitosa',
'nombre': biblioteca.nombre,
'latitud': str(biblioteca.latitud),
'longitud': str(biblioteca.longitud)
})
return Response(
{'error': 'No se pudo geocodificar la dirección'},
status=status.HTTP_400_BAD_REQUEST
)
Crea un archivo nuevo: libros/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .api_views import BibliotecaViewSet
# Router crea automáticamente todas las rutas del ViewSet
router = DefaultRouter()
router.register(r'bibliotecas', BibliotecaViewSet, basename='biblioteca')
# Rutas generadas automáticamente:
# GET /api/bibliotecas/
# POST /api/bibliotecas/
# GET /api/bibliotecas/1/
# PUT /api/bibliotecas/1/
# DELETE /api/bibliotecas/1/
# GET /api/bibliotecas/cercanas/?lat=14&lng=-87
# POST /api/bibliotecas/1/geocodificar/
urlpatterns = [
path('', include(router.urls)),
]
Abre biblioteca_project/urls.py y modifícalo:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls), # Panel de administración
path('api/', include('libros.urls')), # ⭐ Nuestra API
]
# Asegúrate de que el servidor esté corriendo
python manage.py runserver
- Abre tu navegador y ve a:
http://127.0.0.1:8000/api/bibliotecas/ - Verás una interfaz bonita de Django REST Framework
- Haz clic en "GET" para ver todas las bibliotecas en formato JSON
- Prueba crear una nueva desde el formulario HTML que aparece abajo
- Prueba también:
http://127.0.0.1:8000/api/bibliotecas/1/(ver una específica)
🎉 ¡API funcionando!
Ahora tienes una API REST completa que:
- ✅ Lista todas las bibliotecas
- ✅ Crea nuevas bibliotecas con geocodificación automática
- ✅ Permite ver, editar y eliminar bibliotecas
- ✅ Busca bibliotecas cercanas a una ubicación
- ✅ Geocodifica bibliotecas manualmente
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from .models import Biblioteca
from .serializers import BibliotecaSerializer
from .services import GoogleMapsService
# (Ver código completo en PASO 6 arriba)
6. 🎨 Integración Frontend con Mapas
📋 PASO 7: Crear página con mapa interactivo (25 minutos)
🗺️ ¿Qué vamos a hacer?
Crear una página web HTML que:
- ✅ Muestre un mapa de Google Maps
- ✅ Cargue las bibliotecas desde nuestra API
- ✅ Coloque un marcador (pin) en cada biblioteca
- ✅ Muestre información al hacer clic en un marcador
- En la carpeta
libros, crea una nueva carpeta llamadatemplates - Dentro de
templates, crea otra carpeta llamadalibros - Tu estructura debe ser:
libros/templates/libros/
Dentro de libros/templates/libros/, crea un archivo llamado mapa.html
Código Completo del Mapa (con explicaciones paso a paso)
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mapa de Bibliotecas</title>
<!-- Estilos CSS -->
<style>
/* Resetear estilos por defecto */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
}
/* Contenedor principal */
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 15px;
overflow: hidden;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
/* Encabezado */
.header {
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
color: white;
padding: 30px;
text-align: center;
}
/* El mapa ocupará 600px de alto y todo el ancho */
#map {
height: 600px;
width: 100%;
}
/* Panel de información */
.info-panel {
padding: 20px;
background: #f8f9fa;
}
/* Botones */
.btn {
background: #11998e;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
margin: 5px;
}
.btn:hover {
background: #0d7a6f;
}
</style>
</head>
<body>
<div class="container">
<!-- Encabezado -->
<div class="header">
<h1>🗺️ Mapa de Bibliotecas de México</h1>
<p>Encuentra la biblioteca más cercana</p>
</div>
<!-- Panel de controles -->
<div class="info-panel">
<button class="btn" onclick="obtenerMiUbicacion()">
📍 Usar mi ubicación actual
</button>
<button class="btn" onclick="cargarBibliotecas()">
🔄 Recargar bibliotecas
</button>
<span id="contador" style="margin-left: 20px; font-weight: bold;">
Bibliotecas cargadas: 0
</span>
</div>
<!-- Contenedor del mapa (aquí se mostrará Google Maps) -->
<div id="map"></div>
</div>
<!-- Cargar la API de Google Maps -->
<!-- IMPORTANTE: Reemplaza TU_API_KEY con tu clave real -->
<script src="https://maps.googleapis.com/maps/api/js?key=TU_API_KEY&callback=initMap"
async defer></script>
<!-- JavaScript para controlar el mapa -->
<script>
// ========================================
// VARIABLES GLOBALES
// ========================================
let map; // Variable que guardará el objeto del mapa
let markers = []; // Array para guardar todos los marcadores
// ========================================
// FUNCIÓN 1: Inicializar el mapa
// Se ejecuta automáticamente cuando carga Google Maps
// ========================================
function initMap() {
console.log('🗺️ Inicializando mapa...');
// Crear el mapa centrado en Ciudad de México, México
map = new google.maps.Map(document.getElementById('map'), {
center: {
lat: 19.4326, // Latitud de Ciudad de México
lng: -99.1332 // Longitud de Ciudad de México
},
zoom: 4, // Nivel de zoom (1 = mundo, 20 = edificio)
// Opciones adicionales del mapa
mapTypeControl: true, // Mostrar botón para cambiar tipo de mapa
streetViewControl: true, // Mostrar botón Street View
fullscreenControl: true, // Botón de pantalla completa
zoomControl: true // Botones de zoom +/-
});
// Cargar las bibliotecas automáticamente al iniciar
cargarBibliotecas();
}
// ========================================
// FUNCIÓN 2: Cargar bibliotecas desde la API
// ========================================
async function cargarBibliotecas() {
console.log('📚 Cargando bibliotecas desde la API...');
try {
// Hacer petición GET a nuestra API
const response = await fetch('/api/bibliotecas/');
// Verificar si la petición fue exitosa
if (!response.ok) {
throw new Error('Error en la petición: ' + response.status);
}
// Convertir la respuesta a JSON
const data = await response.json();
console.log('✅ Datos recibidos:', data);
// Limpiar marcadores anteriores
limpiarMarcadores();
// Contador de bibliotecas
let contador = 0;
// Crear un marcador para cada biblioteca
data.forEach(biblioteca => {
// Solo crear marcador si tiene coordenadas
if (biblioteca.latitud && biblioteca.longitud) {
crearMarcador(biblioteca);
contador++;
}
});
// Actualizar contador en la página
document.getElementById('contador').textContent =
`Bibliotecas cargadas: ${contador}`;
} catch (error) {
console.error('❌ Error cargando bibliotecas:', error);
alert('Error al cargar las bibliotecas. Verifica la consola.');
}
}
// ========================================
// FUNCIÓN 3: Crear marcador en el mapa
// ========================================
function crearMarcador(biblioteca) {
// Crear el marcador (pin) en el mapa
const marker = new google.maps.Marker({
position: {
lat: parseFloat(biblioteca.latitud), // Convertir string a número
lng: parseFloat(biblioteca.longitud)
},
map: map, // En qué mapa mostrarlo
title: biblioteca.nombre, // Texto al pasar el mouse
animation: google.maps.Animation.DROP // Animación de caída
});
// Crear ventana de información (popup)
const infoWindow = new google.maps.InfoWindow({
content: `
<div style="padding: 10px; max-width: 250px;">
<h3 style="margin: 0 0 10px 0; color: #11998e;">
${biblioteca.nombre}
</h3>
<p style="margin: 5px 0;">
<strong>📍 Dirección:</strong><br>
${biblioteca.direccion}<br>
${biblioteca.ciudad}, ${biblioteca.pais}
</p>
<p style="margin: 5px 0;">
<strong>📞 Teléfono:</strong><br>
${biblioteca.telefono || 'No disponible'}
</p>
<p style="margin: 5px 0;">
<strong>🕒 Horario:</strong><br>
${biblioteca.horario || 'No disponible'}
</p>
<p style="margin: 10px 0 0 0; font-size: 0.9em; color: #666;">
<strong>Coordenadas:</strong><br>
${biblioteca.latitud}, ${biblioteca.longitud}
</p>
${biblioteca.google_maps_url ? `
<p style="margin: 10px 0 0 0; text-align: center;">
<a href="${biblioteca.google_maps_url}"
target="_blank"
style="display: inline-block;
background: #11998e;
color: white;
padding: 8px 15px;
text-decoration: none;
border-radius: 5px;
font-weight: bold;">
🗺️ Abrir en Google Maps
</a>
</p>
` : ''}
</div>
`
});
// Al hacer clic en el marcador, mostrar la información
marker.addListener('click', () => {
// Cerrar todas las ventanas abiertas
markers.forEach(m => {
if (m.infoWindow) {
m.infoWindow.close();
}
});
// Abrir esta ventana
infoWindow.open(map, marker);
// Centrar el mapa en este marcador
map.setCenter(marker.getPosition());
});
// Guardar referencia a la ventana de información
marker.infoWindow = infoWindow;
// Agregar el marcador al array
markers.push(marker);
}
// ========================================
// FUNCIÓN 4: Obtener ubicación del usuario
// ========================================
function obtenerMiUbicacion() {
console.log('📍 Obteniendo ubicación del usuario...');
// Verificar si el navegador soporta geolocalización
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
// Éxito: se obtuvo la ubicación
(position) => {
const pos = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
console.log('✅ Ubicación obtenida:', pos);
// Centrar el mapa en la ubicación del usuario
map.setCenter(pos);
map.setZoom(15);
// Crear marcador "Estoy aquí"
new google.maps.Marker({
position: pos,
map: map,
title: 'Mi ubicación',
icon: {
url: 'http://maps.google.com/mapfiles/ms/icons/blue-dot.png'
}
});
},
// Error: no se pudo obtener la ubicación
(error) => {
console.error('❌ Error obteniendo ubicación:', error);
alert('No se pudo obtener tu ubicación. Verifica los permisos.');
}
);
} else {
alert('Tu navegador no soporta geolocalización');
}
}
// ========================================
// FUNCIÓN 5: Limpiar todos los marcadores
// ========================================
function limpiarMarcadores() {
// Remover cada marcador del mapa
markers.forEach(marker => {
marker.setMap(null);
});
// Vaciar el array
markers = [];
}
</script>
</body>
</html>
Abre libros/views.py y agrega:
from django.shortcuts import render
def mapa_bibliotecas(request):
"""
Vista que muestra el mapa de bibliotecas
"""
return render(request, 'libros/mapa.html')
Modifica libros/urls.py:
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .api_views import BibliotecaViewSet
from .views import mapa_bibliotecas # ⭐ Importar la vista
router = DefaultRouter()
router.register(r'bibliotecas', BibliotecaViewSet, basename='biblioteca')
urlpatterns = [
path('', include(router.urls)),
path('mapa/', mapa_bibliotecas, name='mapa'), # ⭐ Ruta del mapa
]
Abre el archivo libros/templates/libros/mapa.html que acabas de crear:
- Busca la línea (alrededor de la línea 132):
ANTES (línea a buscar):
<script src="https://maps.googleapis.com/maps/api/js?key=TU_API_KEY&callback=initMap" - Reemplaza
TU_API_KEYcon tu clave real de Google Maps (sin comillas):DESPUÉS (tu código):<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyD1234567890abcdefghijklmnopqrstuvwxyz&callback=initMap" - Guarda el archivo (Ctrl + S)
⚠️ Errores comunes:
- ❌ Dejar
TU_API_KEYsin reemplazar → El mapa no cargará - ❌ Poner la clave entre comillas:
key="'AIza...'"→ Error - ❌ Usar una API Key diferente a la de
settings.py→ Funciona, pero es confuso - ✅ Correcto:
key=AIzaSyD1234...(sin comillas, tu clave real)
# Asegúrate de que el servidor esté corriendo
# Si lo detuviste, inícialo nuevamente:
python manage.py runserver
# Deberías ver: "Starting development server at http://127.0.0.1:8000/"
✅ Ahora sí, probemos el mapa:
- Abre tu navegador
- Ve a:
http://127.0.0.1:8000/api/mapa/ - Espera unos segundos mientras carga Google Maps
- Deberías ver:
- ✅ Un mapa de Google Maps centrado en Ciudad de México
- ✅ Dos botones: "📍 Usar mi ubicación actual" y "🔄 Recargar bibliotecas"
- ✅ Un contador: "Bibliotecas cargadas: X"
- ✅ Marcadores (pins rojos) en las ubicaciones de tus bibliotecas
- Haz clic en un marcador → Deberías ver una ventana emergente con:
- Nombre de la biblioteca
- Dirección completa
- Teléfono y horario
- Coordenadas
- Botón verde: "🗺️ Abrir en Google Maps"
- Haz clic en "📍 Usar mi ubicación actual"
- El navegador te pedirá permiso → Haz clic en "Permitir"
- El mapa se centrará en tu ubicación
- Aparecerá un marcador azul: "Mi ubicación"
🚨 Problemas comunes al ver el mapa:
1. El mapa no carga (pantalla gris):
- Presiona
F12para abrir la consola del navegador - Ve a la pestaña "Console"
- Busca errores en rojo:
InvalidKeyMapError→ API Key incorrecta o no reemplazadaApiNotActivatedMapError→ Maps JavaScript API no habilitadaRefererNotAllowedMapError→ Restricciones de la API Key muy estrictas
2. No aparecen los marcadores:
- Presiona
F12→ Pestaña "Console" - Busca:
Bibliotecas cargadas: 0 - Verifica que hayas creado bibliotecas en el admin
- Verifica que las bibliotecas tengan coordenadas (latitud/longitud)
- Prueba:
http://127.0.0.1:8000/api/bibliotecas/→ Deberías ver JSON con tus bibliotecas
3. Error: "Failed to fetch":
- El servidor Django no está corriendo → Ejecuta
python manage.py runserver - La URL es incorrecta → Asegúrate de usar
http://127.0.0.1:8000/api/mapa/
4. "Mi ubicación" no funciona:
- Algunos navegadores requieren HTTPS para geolocalización
- Verifica que diste permiso al navegador
- Si usas Firefox, ve a Configuración → Privacidad → Permisos → Ubicación
✅ Si todo funciona correctamente, deberías poder:
- ✅ Ver el mapa de Google Maps
- ✅ Ver marcadores en cada biblioteca
- ✅ Hacer clic en los marcadores y ver información
- ✅ Usar el zoom y moverse por el mapa
- ✅ Abrir bibliotecas en Google Maps (nueva pestaña)
- ✅ Ver tu ubicación actual en el mapa
🎉 ¡Proyecto Completo!
Felicidades, has creado un sistema completo con:
- ✅ Backend: Django + MySQL + Google Maps API
- ✅ API REST: Endpoints para crear, listar y geocodificar bibliotecas
- ✅ Frontend: Mapa interactivo con Google Maps
- ✅ Geolocalización: Coordenadas automáticas para cada biblioteca
- ✅ Panel Admin: Gestión fácil de bibliotecas
- ✅ Google Maps URL: Enlaces directos a cada ubicación
✅ Checklist Final - Verifica que todo funcione:
🔧 Backend y Base de Datos:
- [ ] Django instalado:
python -m django --versionmuestra 4.2.x - [ ] MySQL corriendo y base de datos
biblioteca_dbcreada - [ ] Conexión a MySQL funciona:
python manage.py dbshell(luegoEXIT;) - [ ] Migraciones aplicadas:
python manage.py showmigrationsmuestra [X] - [ ] Archivo
services.pycreado con el código de Google Maps - [ ] API Key de Google Maps configurada en
settings.py
👨💼 Panel de Administración:
- [ ] Superusuario creado:
python manage.py createsuperuser - [ ] Admin accesible en:
http://127.0.0.1:8000/admin/ - [ ] Sección "Bibliotecas" visible en el admin
- [ ] Puedes agregar una nueva biblioteca
- [ ] Acción "Geocodificar bibliotecas" funciona
- [ ] Coordenadas se llenan automáticamente al geocodificar
- [ ] Campo "Ver en Mapa" muestra enlace clicable
🔌 API REST:
- [ ]
http://127.0.0.1:8000/api/bibliotecas/muestra interfaz de Django REST - [ ] Puedes ver la lista de bibliotecas en formato JSON
- [ ] Puedes crear una biblioteca desde la API
- [ ] Endpoint
/api/bibliotecas/1/muestra una biblioteca específica - [ ] Endpoint
/api/bibliotecas/cercanas/?lat=14&lng=-87funciona
🗺️ Mapa Interactivo:
- [ ]
http://127.0.0.1:8000/api/mapa/carga correctamente - [ ] Mapa de Google Maps se muestra (no pantalla gris)
- [ ] Aparecen marcadores rojos en las bibliotecas
- [ ] Contador muestra: "Bibliotecas cargadas: X" (X > 0)
- [ ] Al hacer clic en un marcador, se muestra la información
- [ ] Botón "Abrir en Google Maps" funciona (abre nueva pestaña)
- [ ] Botón "Usar mi ubicación" funciona (si das permiso)
📊 Prueba Final (Flujo Completo):
- [ ] Crear biblioteca desde el admin con dirección real
- [ ] Geocodificar usando la acción del admin
- [ ] Verificar que se llenaron latitud, longitud y google_maps_url
- [ ] Hacer clic en "Ver en Mapa" → Abre Google Maps en la ubicación correcta
- [ ] Ir a
/api/mapa/→ Ver el marcador en el mapa - [ ] Hacer clic en el marcador → Ver toda la información
- [ ] Verificar en
/api/bibliotecas/que el JSON incluye todas las coordenadas
🚨 ¿Las Coordenadas No Aparecen?
Si al crear una biblioteca los campos Latitud, Longitud y Place ID aparecen vacíos (-), revisa el archivo:
📄 SOLUCION_COORDENADAS_NO_APARECEN.md
Este documento contiene 8 causas comunes y sus soluciones paso a paso:
- No se instaló la biblioteca
googlemaps - API Key no configurada o incorrecta
- Geocoding API no habilitada
- ⭐ Facturación no configurada (la más común)
- Archivo
services.pyno existe - Método
geocodificar()falta en el modelo - Serializer no llama a
geocodificar() - Restricciones de la API Key
💡 Tip: El problema más frecuente es no tener configurada la facturación en Google Cloud, aunque Google ofrece $200 USD gratis al mes.
🚀 Mejoras que puedes agregar:
- Búsqueda por ubicación: Agregar un campo de búsqueda para encontrar bibliotecas por ciudad
- Calcular distancias: Mostrar la distancia desde tu ubicación a cada biblioteca
- Filtros: Filtrar bibliotecas por ciudad, horario, etc.
- Rutas: Mostrar cómo llegar a una biblioteca
- Compartir: Agregar botón para compartir la ubicación de una biblioteca
📋 Comandos Rápidos de Referencia
Iniciar/Detener Proyecto:
# Activar entorno virtual
.\venv\Scripts\Activate
# Iniciar servidor
python manage.py runserver
# Detener servidor
# Presiona: Ctrl + C
# Desactivar entorno virtual
deactivate
Trabajar con la Base de Datos:
# Crear migraciones después de cambiar models.py
python manage.py makemigrations
# Aplicar migraciones
python manage.py migrate
# Ver estado de migraciones
python manage.py showmigrations
# Abrir shell de Django (para pruebas)
python manage.py shell
# Conectarse a la base de datos MySQL
python manage.py dbshell
Gestión de Usuarios:
# Crear nuevo superusuario
python manage.py createsuperuser
# Cambiar contraseña de usuario
python manage.py changepassword admin
Pruebas con la API:
# Listar todas las bibliotecas (JSON)
Invoke-WebRequest -Uri "http://127.0.0.1:8000/api/bibliotecas/"
# Ver una biblioteca específica
Invoke-WebRequest -Uri "http://127.0.0.1:8000/api/bibliotecas/1/"
# Crear biblioteca (POST)
$body = @{
nombre = "Test"
direccion = "Circuito Interior s/n, Ciudad Universitaria"
ciudad = " Ciudad de México"
pais = "México"
} | ConvertTo-Json
Invoke-RestMethod -Uri "http://127.0.0.1:8000/api/bibliotecas/" `
-Method POST `
-Body $body `
-ContentType "application/json"
Solución de Problemas:
# Verificar versión de Python
python --version
# Verificar versión de Django
python -m django --version
# Listar paquetes instalados
pip list
# Ver configuración de Django
python manage.py check
# Reinstalar dependencias desde requirements.txt
pip install -r requirements.txt
# Limpiar cache de Python
python manage.py clean_pyc
# Verificar integridad de la base de datos
python manage.py check --database default
📚 URLs Importantes del Proyecto
| URL | Descripción |
|---|---|
http://127.0.0.1:8000/ |
Página principal (puede mostrar error si no hay vista raíz) |
http://127.0.0.1:8000/admin/ |
Panel de administración de Django |
http://127.0.0.1:8000/api/bibliotecas/ |
API REST - Listar todas las bibliotecas |
http://127.0.0.1:8000/api/bibliotecas/1/ |
API REST - Ver biblioteca con ID 1 |
http://127.0.0.1:8000/api/bibliotecas/cercanas/?lat=14&lng=-87 |
API REST - Buscar bibliotecas cercanas |
http://127.0.0.1:8000/api/mapa/ |
Mapa interactivo con Google Maps |
🎓 Entrega de Práctica - Evidencias Requeridas
Para completar tu práctica, muestra en pantalla lo siguiente:
- Muestra 1: Panel de admin mostrando la lista de bibliotecas con coordenadas
- Muestra 2: Formulario de una biblioteca con latitud, longitud y google_maps_url llenos
- Muestra 3: Mapa interactivo (
/api/mapa/) mostrando marcadores - Muestra 4: Popup de información al hacer clic en un marcador
- Muestra 5: Respuesta JSON de la API (
/api/bibliotecas/) - Muestra 6: Google Maps abierto desde el enlace (pestaña nueva)
- Muestra 7: Consola de Django mostrando el servidor corriendo sin errores
📝 Incluye también:
- Archivo
requirements.txtcon todas las dependencias - Código de
models.py,services.py,admin.py - Breve reporte explicando qué hace cada componente
7. 🔥 Funcionalidades Avanzadas
7.1 Búsqueda de Lugares Cercanos
@classmethod
def buscar_lugares_cercanos(cls, lat, lng, tipo='library', radio=5000):
"""
Busca lugares cercanos a una ubicación
Args:
lat: Latitud
lng: Longitud
tipo: Tipo de lugar (library, restaurant, school, etc.)
radio: Radio de búsqueda en metros
"""
try:
service = cls()
resultado = service.client.places_nearby(
location=(float(lat), float(lng)), # Ubicación central
radius=radio, # Radio en metros
type=tipo # Tipo de lugar
)
lugares = []
for place in resultado.get('results', []):
lugares.append({
'nombre': place.get('name'),
'direccion': place.get('vicinity'),
'rating': place.get('rating', 0),
'place_id': place.get('place_id'),
'lat': place['geometry']['location']['lat'],
'lng': place['geometry']['location']['lng']
})
return lugares
except Exception as e:
logger.error(f"Error buscando lugares: {e}")
return []
7.2 Calcular Ruta entre Dos Puntos
function mostrarRuta(origen, destino) {
// Crear servicio de direcciones
const directionsService = new google.maps.DirectionsService();
const directionsRenderer = new google.maps.DirectionsRenderer();
directionsRenderer.setMap(map); // Asocia al mapa
// Solicitar ruta
directionsService.route({
origin: origen, // Punto de inicio
destination: destino, // Punto de destino
travelMode: google.maps.TravelMode.DRIVING // Modo: DRIVING, WALKING, BICYCLING, TRANSIT
}, (response, status) => {
if (status === 'OK') {
directionsRenderer.setDirections(response); // Dibuja la ruta
// Obtener información de la ruta
const route = response.routes[0].legs[0];
console.log(`Distancia: ${route.distance.text}`); // Muestra distancia
console.log(`Duración: ${route.duration.text}`); // Muestra tiempo
} else {
console.error('Error calculando ruta:', status);
}
});
}
8. 🛡️ Mejores Prácticas y Seguridad
🔐 Seguridad de API Keys
❌ NUNCA hagas esto:
- Subir API Keys a repositorios públicos (GitHub, GitLab)
- Hardcodear API Keys directamente en código JavaScript del cliente
- Compartir API Keys en foros, chats o correos electrónicos
- Usar la misma API Key para desarrollo y producción
- Dejar API Keys sin restricciones de uso
✅ SÍ haz esto:
- Variables de Entorno:
settings.py (Opción Segura)
import os from decouple import config # pip install python-decouple # Leer de variable de entorno o archivo .env GOOGLE_MAPS_API_KEY = config('GOOGLE_MAPS_API_KEY', default='') # Verificar que esté configurada if not GOOGLE_MAPS_API_KEY: raise ValueError("GOOGLE_MAPS_API_KEY no configurada").env (en raíz del proyecto)GOOGLE_MAPS_API_KEY=AIzaSyD1234567890abcdefghijklmnopqrstuvwxyz DEBUG=True SECRET_KEY=django-insecure-tu-secret-key-aqui.gitignore (IMPORTANTE)# Archivos a NO subir a GitHub .env *.pyc __pycache__/ venv/ db.sqlite3 /staticfiles/ - Restricciones de API Key en Google Cloud:
- Ve a Google Cloud Console → Credenciales
- Clic en tu API Key → Editar
- Restricciones de aplicación:
- Sitios web: Restringir por dominio (tuapp.com/*)
- Direcciones IP: Solo desde tu servidor (123.45.67.89)
- Restricciones de API: Selecciona solo las APIs que uses
- ✓ Geocoding API
- ✓ Maps JavaScript API
- ✗ (Desmarcar las demás)
- Proxy Backend para API Keys:
Patrón Proxy (Seguro)
# NUNCA exponer API Key en frontend JavaScript # En lugar de: # ❌ <script src="...maps/api/js?key=AIza..."></script> # Usar proxy en backend: # ✅ Frontend llama a tu API → Django llama a Google Maps # libros/views.py @api_view(['POST']) def geocodificar_proxy(request): direccion = request.data.get('direccion') # API Key está en settings.py (servidor), no en JavaScript resultado = GoogleMapsService.geocodificar_direccion(direccion) return Response(resultado)
🚀 Optimización de Rendimiento
1. Caché de Resultados de Geocodificación
def save(self, *args, **kwargs):
"""Solo geocodificar si la dirección cambió"""
if self.pk: # Si ya existe (actualización)
old = Biblioteca.objects.get(pk=self.pk)
# Verificar si cambió la dirección
direccion_cambio = (
old.direccion != self.direccion or
old.ciudad != self.ciudad or
old.pais != self.pais
)
if direccion_cambio:
self.geocodificar() # Solo si cambió
else: # Nuevo registro
self.geocodificar()
super().save(*args, **kwargs)
2. Índices en Base de Datos
class Biblioteca(models.Model):
nombre = models.CharField(max_length=200, db_index=True) # Índice
ciudad = models.CharField(max_length=100, db_index=True) # Índice
latitud = models.DecimalField(max_digits=10, decimal_places=7)
longitud = models.DecimalField(max_digits=10, decimal_places=7)
class Meta:
# Índice compuesto para búsquedas geoespaciales
indexes = [
models.Index(fields=['latitud', 'longitud']),
models.Index(fields=['ciudad', 'nombre']),
]
3. Paginación en API
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 50 # Máximo 50 bibliotecas por petición
}
4. Lazy Loading de Mapas
// Solo cargar marcadores visibles en el viewport
function mostrarMarcadoresVisibles() {
const bounds = map.getBounds();
bibliotecas.forEach(bib => {
const posicion = { lat: bib.latitud, lng: bib.longitud };
// Solo crear marcador si está en el viewport actual
if (bounds.contains(posicion)) {
crearMarcador(bib);
}
});
}
📊 Monitoreo y Logging
import logging
logger = logging.getLogger(__name__)
@classmethod
def geocodificar_direccion(cls, direccion):
logger.info(f"Geocodificando: {direccion}")
try:
service = cls()
resultado = service.client.geocode(direccion)
if resultado:
logger.info(f"✅ Geocodificación exitosa: {direccion}")
return {...}
else:
logger.warning(f"⚠️ No se encontró: {direccion}")
return None
except Exception as e:
logger.error(f"❌ Error: {direccion} - {str(e)}")
return None
9. 💡 Casos de Uso Avanzados
1. Búsqueda de Bibliotecas Cercanas con Radio
Objetivo: Encontrar bibliotecas dentro de un radio específico desde la ubicación del usuario.
from math import radians, cos, sin, asin, sqrt
@classmethod
def buscar_cercanas(cls, lat_usuario, lng_usuario, radio_km=5):
"""
Busca bibliotecas dentro de un radio específico
Args:
lat_usuario: Latitud del usuario
lng_usuario: Longitud del usuario
radio_km: Radio de búsqueda en kilómetros
Returns:
Lista de bibliotecas ordenadas por distancia
"""
from .models import Biblioteca
# Obtener todas las bibliotecas con coordenadas
bibliotecas = Biblioteca.objects.filter(
latitud__isnull=False,
longitud__isnull=False
)
resultados = []
for bib in bibliotecas:
# Calcular distancia usando Haversine
distancia = cls.calcular_distancia_haversine(
lat_usuario, lng_usuario,
float(bib.latitud), float(bib.longitud)
)
# Solo incluir si está dentro del radio
if distancia <= radio_km:
resultados.append({
'biblioteca': bib,
'distancia_km': round(distancia, 2)
})
# Ordenar por distancia (más cercano primero)
resultados.sort(key=lambda x: x['distancia_km'])
return resultados
@staticmethod
def calcular_distancia_haversine(lat1, lon1, lat2, lon2):
"""Calcula distancia entre dos coordenadas (fórmula Haversine)"""
R = 6371 # Radio de la Tierra en km
# Convertir a radianes
lat1, lon1, lat2, lon2 = map(radians, [lat1, lon1, lat2, lon2])
# Diferencias
dlat = lat2 - lat1
dlon = lon2 - lon1
# Fórmula Haversine
a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
c = 2 * asin(sqrt(a))
return R * c # Distancia en km
2. Ruta Optimizada entre Múltiples Bibliotecas
Objetivo: Calcular la ruta más eficiente para visitar varias bibliotecas.
@classmethod
def calcular_ruta_optimizada(cls, ids_bibliotecas):
"""Calcula ruta óptima usando Google Directions API"""
from .models import Biblioteca
# Obtener bibliotecas
bibliotecas = Biblioteca.objects.filter(id__in=ids_bibliotecas)
if len(bibliotecas) < 2:
return None
service = cls()
# Crear lista de waypoints
waypoints = [
(float(bib.latitud), float(bib.longitud))
for bib in bibliotecas
]
# Calcular ruta optimizada
ruta = service.client.directions(
origin=waypoints[0], # Inicio
destination=waypoints[-1], # Fin
waypoints=waypoints[1:-1], # Puntos intermedios
optimize_waypoints=True, # ⭐ Optimizar orden
mode='driving'
)
return ruta
3. Agrupación de Bibliotecas (Clustering)
Objetivo: Agrupar bibliotecas cercanas para mejorar rendimiento del mapa.
// Cargar biblioteca de clustering
// <script src="https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js"></script>
let markerCluster;
function crearCluster() {
// Agrupar marcadores cercanos
markerCluster = new markerClusterer.MarkerClusterer({
map,
markers,
// Configuración del cluster
gridSize: 50, // Tamaño de la cuadrícula
maxZoom: 15, // Zoom máximo antes de separar
algorithm: new markerClusterer.SuperClusterAlgorithm({
radius: 100 // Radio de agrupación
})
});
}
10. 📚 Recursos Adicionales y Referencias
📖 Documentación Oficial
- Google Maps Platform: developers.google.com/maps/documentation
- Geocoding API Reference: developers.google.com/maps/documentation/geocoding
- Maps JavaScript API: developers.google.com/maps/documentation/javascript
- Django Documentation: docs.djangoproject.com/en/4.2/
- Django REST Framework: django-rest-framework.org
- Python googlemaps: github.com/googlemaps/google-maps-services-python
🎓 Tutoriales y Cursos Recomendados
- Google Maps Platform Training: Cursos gratuitos de Google
- Django for Beginners: djangoforbeginners.com
- MDN Web Docs - Geolocation API: developer.mozilla.org
- Real Python - Django Tutorials: realpython.com/tutorials/django/
🛠️ Herramientas Útiles
| Herramienta | Descripción | URL |
|---|---|---|
| Postman | Prueba de APIs REST | postman.com |
| DB Browser for SQLite | Explorar bases de datos | sqlitebrowser.org |
| VS Code Extensions | Python, Django, REST Client | marketplace.visualstudio.com |
| Google Maps Styling Wizard | Personalizar colores del mapa | mapstyle.withgoogle.com |
| JSON Formatter | Formatear y validar JSON | jsonformatter.org |
💼 Proyectos de Ejemplo para Inspiración
- Sistema de Delivery con Rutas Optimizadas
- Geocodificar direcciones de clientes
- Calcular rutas de entrega
- Estimación de tiempos
- Buscador de Servicios Públicos
- Hospitales, escuelas, parques
- Filtrado por distancia
- Comparación de horarios
- App de Eventos Locales
- Mapa de eventos cercanos
- Notificaciones geográficas
- Compra de tickets integrada
- Sistema de Rastreo de Vehículos
- Tracking en tiempo real
- Historial de rutas
- Alertas de geofencing
8. 📖 Glosario de Términos
Términos Técnicos de Geolocalización
Conjunto de funciones y procedimientos que permite la comunicación entre dos aplicaciones de software.
Par de números (latitud, longitud) que identifica una ubicación exacta en la Tierra.
Proceso de convertir una dirección postal en coordenadas geográficas (lat, lng).
Proceso opuesto: convertir coordenadas geográficas en una dirección postal legible.
Sistema de navegación por satélite que proporciona información de ubicación y tiempo.
Distancia angular medida en grados desde el ecuador (0°) hacia los polos (-90° a +90°).
Distancia angular medida en grados desde el meridiano de Greenwich (0°) (-180° a +180°).
Identificador único de Google Maps para un lugar específico en el mundo.
Icono o pin que se muestra en un mapa para indicar una ubicación específica.
Ventana emergente que muestra información adicional sobre un marcador en el mapa.
API que calcula distancias y tiempos de viaje entre múltiples orígenes y destinos.
API que proporciona direcciones de navegación entre ubicaciones.