Cuando trabajas con mapas web, una de las primeras optimizaciones que debes implementar es el cacheo de tiles. Si tu mapa recibe más de 100 visitas diarias, estás desperdiciando recursos del servidor y dando una experiencia lenta al usuario.
Hoy comparamos las dos soluciones más usadas para cachear tiles: TileCache y MapProxy. Con números reales y configuraciones listas para copiar y pegar.
¿Qué es el tile caching?
Cuando un usuario ve tu mapa, el navegador solicita cientos de imágenes pequeñas (llamadas tiles) que se ensamblan para formar el mapa completo.
Sin caché:
- Usuario solicita tile -> GeoServer procesa datos -> Genera imagen -> Envía tile.
- Tiempo: 200-500ms aproximado por tile.
- CPU: Alto consumo.
- Otro usuario pide el mismo tile: Se repite todo el proceso.
Con caché:
- Usuario solicita tile -> MapProxy verifica caché -> Si existe, lo envía.
- Tiempo: 5-20ms aproximado por tile.
- CPU: Mínimo.
- Otro usuario: Recibe el mismo tile cacheado instantáneamente.
TileCache vs MapProxy: Comparemos
TileCache
Que es lo bueno:
- Configuración simple (archivo .cfg de 10 líneas).
- Ligero en recursos.
- Perfecto para proyectos pequeños.
Que es lo malo:
- Sin desarrollo activo desde 2014.
- Usa Python 2.
- No soporta reproyección al vuelo.
- Formatos limitados, solo soporta PNG, JPEG.
- Sin interfaz de administración.
MapProxy
Que es lo bueno:
- Desarrollo activo. Ultima versión del 2024.
- Python 3.
- Reproyección automática entre sistemas de coordenadas.
- Múltiples formatos (PNG, JPEG, WebP, GeoTIFF).
- Interfaz web de administración.
- Puede funcionar como proxy WMS/WMTS.
- Seeding inteligente (precacheo selectivo).
- Caché en disco, SQLite, MBTiles, S3, Redis.
Que es lo malo:
- Configuración más compleja
- Consume más recursos aunque sigue siendo ligero.
Comparación de velocidad: Números reales
Prueba realizada con GeoServer sirviendo capa vectorial de barrios de Cali (10,000 polígonos):
Sin caché usando GeoServer directo
Zoom 10: 450ms por tile
Zoom 14: 720ms por tile
Zoom 16: 1200ms por tile
CPU: 85-95%
Con TileCache
Primera solicitud (cache miss): 480ms
Solicitudes posteriores: 8-15ms
CPU: 5-10%
Reducción: 97% en tiempo
Con MapProxy
Primera solicitud (cache miss): 420ms
Solicitudes posteriores: 5-12ms
CPU: 3-8%
Reducción: 98% en tiempo
Bonus: Reproyección EPSG:4326 a EPSG:3857 sin costo extra
Ganador: MapProxy.
Instalación de MapProxy
En Ubuntu/Debian:
en terminal de comandos
# Instalar dependencias
sudo apt update
sudo apt install python3-pip python3-pil python3-yaml libgeos-dev
# Instalar MapProxy
pip3 install MapProxy
# Verificar instalación
mapproxy-util --version
En Docker (es mejor para producción):
en terminal de comandos
docker pull mapproxy/mapproxy:latest
docker run -d \
--name mapproxy \
-p 8080:8080 \
-v $(pwd)/mapproxy:/mapproxy \
mapproxy/mapproxy
Configuración básica de MapProxy
Crea el archivo mapproxy.yaml:
Tu archivo yaml
services:
demo:
tms:
use_grid_names: true
kml:
use_grid_names: true
wmts:
wms:
srs: ['EPSG:4326', 'EPSG:3857', 'EPSG:3116']
image_formats: ['image/png', 'image/jpeg', 'image/webp']
layers:
- name: barrios_cali
title: Barrios de Cali
sources: [barrios_cache]
caches:
barrios_cache:
grids: [webmercator, geograficas]
sources: [barrios_wms]
cache:
type: file
directory_layout: tms
format: image/png
request_format: image/png
sources:
barrios_wms:
type: wms
req:
url: http://localhost:8080/geoserver/wms
layers: cali:barrios
transparent: true
coverage:
bbox: [-76.6, 3.3, -76.4, 3.6]
srs: 'EPSG:4326'
grids:
webmercator:
srs: 'EPSG:3857'
origin: nw
geograficas:
srs: 'EPSG:4326'
origin: nw
globals:
cache:
base_dir: './cache_data'
lock_dir: './cache_data/locks'
image:
resampling_method: bilinear
paletted: false
Iniciar MapProxy
en terminal de comandos
# Crear directorio de caché
mkdir cache_data
# Validar configuración
mapproxy-util validate mapproxy.yaml
# Iniciar servidor
mapproxy-util serve-develop mapproxy.yaml
# Acceder a:
# - Demo: http://localhost:8080/demo/
# - WMS: http://localhost:8080/service?
# - WMTS: http://localhost:8080/wmts/1.0.0/WMTSCapabilities.xml
Integración con Leaflet
tu archivo javascript
// Usando tiles cacheados de MapProxy
const map = L.map('map').setView([3.4516, -76.5320], 13);
// TMS
L.tileLayer('http://localhost:8080/tiles/barrios_cache/webmercator/{z}/{x}/{y}.png', {
attribution: 'Datos: Alcaldía de Cali',
tms: true // Importante: TMS usa origen inferior izquierdo
}).addTo(map);
Seeding: Precachear tiles
El “seeding” es generar tiles antes de que los usuarios los soliciten. Esto es muy útil para:
- Lanzamiento de aplicaciones. Dejas la caché lista desde el día 1.
- Actualizaciones de datos para regenerar caché afectada.
- Áreas de interés. Solo cachear zonas relevantes.
Crear archivo seed.yaml:
Tu archivo yaml
seeds:
barrios_seed:
caches: [barrios_cache]
grids: [webmercator]
coverages: [cali_centro]
levels:
from: 10
to: 16
coverages:
cali_centro:
bbox: [-76.5500, 3.4200, -76.5100, 3.4700]
srs: 'EPSG:4326'
Ejecutar seeding:
en terminal de comandos
# Seed completo
mapproxy-seed -f mapproxy.yaml -s seed.yaml
# Solo mostrar estadísticas (no genera tiles)
mapproxy-seed -f mapproxy.yaml -s seed.yaml --dry-run
# Continuar seed interrumpido
mapproxy-seed -f mapproxy.yaml -s seed.yaml --continue
# Usar 4 procesos paralelos (más rápido)
mapproxy-seed -f mapproxy.yaml -s seed.yaml -c 4
Configuración avanzada: Múltiples capas
Tu archivo yaml
layers:
- name: mapa_base
title: Mapa base completo
sources: [calles_cache, barrios_cache, limites_cache]
caches:
calles_cache:
sources: [calles_wms]
grids: [webmercator]
barrios_cache:
sources: [barrios_wms]
grids: [webmercator]
meta_size: [4, 4]
meta_buffer: 10
limites_cache:
sources: [limites_wms]
grids: [webmercator]
format: image/png8
sources:
calles_wms:
type: wms
req:
url: http://geoserver:8080/wms
layers: cali:calles
barrios_wms:
type: wms
req:
url: http://geoserver:8080/wms
layers: cali:barrios
limites_wms:
type: wms
req:
url: http://geoserver:8080/wms
layers: cali:limites
Optimización de almacenamiento con MBTiles
MBTiles es un formato de base de datos SQLite que guarda tiles de forma compacta:
Tu archivo yaml
caches:
barrios_cache:
sources: [barrios_wms]
grids: [webmercator]
cache:
type: mbtiles
filename: cache_data/barrios.mbtiles
Ventajas de MBTiles:
- 30 a 50% menos espacio que archivos individuales.
- Más rápido en SSD.
- Portable.
- Compatible con aplicaciones móviles offline.
Desventajas:
- Locks de escritura. Problemas con concurrencia alta.
- Más difícil de depurar.
¿Cuándo vale la pena cachear tiles?
SÍ necesitas caché cuando:
- Tu mapa recibe más de 50 visitas diarias.
- Usas capas con renderizado complejo con muchos polígonos y estilos pesados.
- Tu servidor tiene recursos limitados.
- Tus datos no cambian constantemente.
- Quieres servir tiles en múltiples proyecciones.
- Necesitas mapas offline o para apps móviles.
NO necesitas caché cuando:
- Tus datos cambian cada minuto.
- Tu mapa es dinámico porque tiene filtros o consultas personalizadas por usuario.
- Tienes menos de 20 visitas diarias.
- Usas tiles base externos de openStreetMap, Google o Mapbox.
- Tu servidor tiene recursos sobrados y las capas son simples.
Limpieza de caché
Con el tiempo el caché crece por lo que debes aplicar limpieza:
en terminal de comandos
# Ver tamaño del caché
du -sh cache_data/
# Limpiar tiles antiguos de más de 30 días
mapproxy-seed -f mapproxy.yaml -s seed.yaml --cleanup --older-than="30d"
# Limpiar zoom específico
mapproxy-seed -f mapproxy.yaml -s seed.yaml --cleanup --levels=17,18
Monitoreo de MapProxy
MapProxy no tiene dashboard built-in, pero puedes monitorearlo así:
en terminal de comandos
# Ver logs
tail -f /var/log/mapproxy.log
# Estadísticas básicas con Apache/Nginx access logs
awk '{print $7}' /var/log/nginx/mapproxy_access.log | grep -E "/tiles/" | wc -l
Configuración de producción con Nginx
nginx
upstream mapproxy {
server localhost:8080;
}
server {
listen 80;
server_name tiles.midominio.com;
location / {
proxy_pass http://mapproxy;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# Caché en Nginx
proxy_cache tiles_cache;
proxy_cache_valid 200 30d;
proxy_cache_valid 404 1m;
add_header X-Cache-Status $upstream_cache_status;
}
}
# Definir zona de caché
proxy_cache_path /var/cache/nginx/tiles
levels=1:2
keys_zone=tiles_cache:100m
max_size=10g
inactive=30d;
