Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    f4tb0yz

    map

    f4tb0yz/map
    Design
    1 installs

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    Especialista en integración de Mapbox y geolocalización. Maneja visualización de mapas, tracking de ubicación, rutas visuales y optimización de geocoding.

    SKILL.md

    Responsabilidad Única

    Todo lo relacionado con Mapbox, geolocalización y visualización de mapas. NO incluye lógica de negocio de rutas ni optimizaciones generales.


    Mapbox Setup & Configuration

    SDK

    • Mapbox Maps SDK: Para visualización de mapas
    • Mapbox Search API: Para geocoding y búsqueda de direcciones
    • Versión: Usar última versión estable

    Estilo de Mapa

    • Dark Style: Usar mapbox://styles/mapbox/dark-v11 para coherencia con Neon-Dark theme
    • Custom Style: Opcionalmente crear estilo custom con colores neon
    • Zoom levels: Min 10 (ciudad), Max 18 (calle)

    Configuración Inicial

    MapboxMap(
      styleString: 'mapbox://styles/mapbox/dark-v11',
      cameraOptions: CameraOptions(
        center: Point(coordinates: Position(-74.3636, 4.3369)), // Fusagasugá
        zoom: 13.0,
      ),
      onMapCreated: _onMapCreated,
    )
    

    Offline Maps (Tile Caching)

    Bounding Box Fusagasugá

    Definir área de descarga para tiles offline:

    final fusagasugaBounds = CoordinateBounds(
      southwest: Point(coordinates: Position(-74.4136, 4.2869)),
      northeast: Point(coordinates: Position(-74.3136, 4.3869)),
    );
    

    Estrategia de Descarga

    • Cuándo: Al detectar WiFi o al iniciar ruta por primera vez
    • Zoom levels: 12-16 (balance entre detalle y tamaño)
    • Tipo: Vectorial (más ligero y escalable que raster)
    • Tamaño estimado: ~50-100 MB para Fusagasugá

    Implementación

    final offlineManager = await OfflineManager.getInstance();
    
    await offlineManager.downloadRegion(
      OfflineRegionDefinition(
        styleURL: 'mapbox://styles/mapbox/dark-v11',
        bounds: fusagasugaBounds,
        minZoom: 12.0,
        maxZoom: 16.0,
      ),
      metadata: {'region': 'fusagasuga'},
    );
    

    Reglas de Negocio - Offline

    • No descargar en datos móviles (solo WiFi)
    • Actualizar tiles cada 30 días
    • Permitir descarga manual desde configuración
    • Mostrar progreso de descarga al usuario

    Location Tracking

    Configuración de Precisión

    LocationSettings(
      accuracy: LocationAccuracy.high,
      distanceFilter: 10, // Actualizar cada 10 metros
      timeLimit: Duration(seconds: 5),
    )
    

    Location Marker (User Icon)

    • Tipo: PointAnnotation para mejor performance
    • Icono: Custom icon con flecha direccional
    • Rotación: Basada en heading del compass
    • Smoothing: Aplicar LERP para rotación suave
    // Actualizar solo si movimiento significativo
    void updateLocationMarker(Position position, double heading) {
      final distance = _calculateDistance(_lastPosition, position);
      final headingDiff = (heading - _lastHeading).abs();
      
      if (distance < 1.0 && headingDiff < 4.0) {
        return; // No actualizar si cambio es mínimo
      }
      
      // Smooth rotation con LERP
      final smoothHeading = _lerp(_lastHeading, heading, 0.3);
      
      _locationAnnotation?.geometry = Point(coordinates: position);
      _locationAnnotation?.iconRotate = smoothHeading;
    }
    
    double _lerp(double start, double end, double t) {
      return start + (end - start) * t;
    }
    

    Optimizaciones de Location

    • Threshold de 1 metro para actualizar posición
    • Threshold de 4 grados para actualizar rotación
    • Pausar tracking cuando app está en background
    • Reanudar tracking al volver a tab de mapa

    Polylines y Rutas Visuales

    Gradient Polyline (Progreso de Ruta)

    Visualizar ruta con gradiente que cambia según progreso:

    LineLayer(
      id: 'route-layer',
      sourceId: 'route-source',
      lineColor: [
        'interpolate',
        ['linear'],
        ['line-progress'],
        0, '#00FFFF', // Neon Cyan (inicio)
        1, '#39FF14', // Neon Green (fin)
      ],
      lineWidth: 5.0,
      lineGradient: true,
    )
    

    Progress Tracking

    • Limpiar segmentos ya recorridos para liberar memoria
    • Actualizar gradiente en tiempo real
    • Mostrar puntos de entrega como markers

    Implementación

    void updateRouteProgress(double progress) {
      // progress: 0.0 - 1.0
      final completedSegments = _routeCoordinates.take(
        (_routeCoordinates.length * progress).round()
      ).toList();
      
      _mapController.updateSource(
        'route-source',
        GeoJsonSource(
          data: LineString(coordinates: completedSegments).toJson(),
        ),
      );
    }
    

    Markers y Annotations

    Package Markers (Pins Numerados)

    Mostrar paquetes en el mapa con números:

    PointAnnotation(
      id: 'package-${package.id}',
      geometry: Point(coordinates: package.coordinates),
      iconImage: 'custom-pin', // Custom image con número
      iconSize: 1.0,
      iconAnchor: IconAnchor.BOTTOM,
    )
    

    Custom Pin con Número

    Generar imagen de pin con número dinámicamente:

    Future<Uint8List> generateNumberedPin(int number, Color neonColor) async {
      final recorder = ui.PictureRecorder();
      final canvas = Canvas(recorder);
      
      // Dibujar pin con número
      // ... código de drawing
      
      final picture = recorder.endRecording();
      final image = await picture.toImage(60, 80);
      final bytes = await image.toByteData(format: ui.ImageByteFormat.png);
      return bytes!.buffer.asUint8List();
    }
    

    Reglas de Markers

    • Mostrar solo paquetes de ruta activa
    • Color según estado (pending: orange, delivered: green, failed: red)
    • Tap en marker abre detalles del paquete
    • Cluster markers si hay >20 paquetes cercanos

    Geocoding y Búsqueda de Direcciones

    Capa de Caché Local

    Antes de consultar API, buscar en caché local:

    Future<Address?> searchAddress(String query) async {
      // 1. Normalizar query
      final normalizedQuery = query.trim().toLowerCase();
      
      // 2. Buscar en caché local
      final cached = await _cacheRepository.findAddress(normalizedQuery);
      if (cached != null && _isCacheValid(cached.timestamp)) {
        return cached;
      }
      
      // 3. Consultar API de Mapbox
      final result = await _mapboxSearchAPI.search(
        query: normalizedQuery,
        proximity: Position(-74.3636, 4.3369), // Fusagasugá
        bbox: fusagasugaBounds,
        limit: 5,
      );
      
      // 4. Guardar en caché
      if (result != null) {
        await _cacheRepository.saveAddress(result);
      }
      
      return result;
    }
    
    bool _isCacheValid(DateTime timestamp) {
      return DateTime.now().difference(timestamp).inDays < 30;
    }
    

    Optimización de Búsquedas

    Smart Debouncing

    Timer? _debounceTimer;
    
    void onSearchTextChanged(String text) {
      _debounceTimer?.cancel();
      
      // No buscar si cambio no es significativo
      if (text.trim() == _lastQuery.trim()) return;
      
      _debounceTimer = Timer(Duration(milliseconds: 500), () {
        _performSearch(text);
      });
    }
    

    Bounding Box Estricto

    Limitar búsquedas a Fusagasugá y Cundinamarca:

    final searchBounds = CoordinateBounds(
      southwest: Position(-74.5, 4.0),
      northeast: Position(-74.0, 4.5),
    );
    

    Limitación de Resultados

    Pedir solo 5 resultados más relevantes para ahorrar ancho de banda:

    final results = await searchAPI.search(
      query: query,
      limit: 5,
      types: ['address', 'place'],
    );
    

    Lazy Geocoding

    Estrategia

    No geocodificar todos los paquetes al cargar lista, solo cuando sea necesario:

    class PackageListItem extends StatefulWidget {
      final Package package;
      
      @override
      Widget build(BuildContext context) {
        return VisibilityDetector(
          key: Key('package-${package.id}'),
          onVisibilityChanged: (info) {
            if (info.visibleFraction > 0.5) {
              // Item visible, geocodificar si no tiene coordenadas
              _geocodeIfNeeded();
            }
          },
          child: PackageCard(package: package),
        );
      }
    }
    

    Reglas de Lazy Geocoding

    • Geocodificar solo cuando paquete entra en viewport
    • Geocodificar cuando usuario abre mapa de paquete específico
    • Cachear resultados para no repetir consultas
    • Priorizar paquetes de ruta activa

    Coordinate Compression

    Precisión Óptima

    Almacenar coordenadas con 6 decimales (precisión de ~10cm):

    double compressCoordinate(double coordinate) {
      return double.parse(coordinate.toStringAsFixed(6));
    }
    
    Position compressPosition(Position position) {
      return Position(
        compressCoordinate(position.lng),
        compressCoordinate(position.lat),
      );
    }
    

    Beneficios

    • Reduce tamaño de base de datos local
    • Suficiente precisión para logística
    • Mejora performance de sync

    Gestión de Errores

    Fallback Offline

    Si API falla o no hay internet:

    Future<List<Address>> searchWithFallback(String query) async {
      try {
        // Intentar API
        return await _mapboxSearchAPI.search(query);
      } catch (e) {
        // Fallback a búsqueda local parcial
        return await _cacheRepository.searchPartialMatch(query);
      }
    }
    

    Manejo de Errores de Mapbox

    • Network error: Usar caché local
    • Invalid coordinates: Mostrar error al usuario
    • Rate limit: Implementar exponential backoff
    • Invalid API key: Notificar error crítico

    Optimización de Performance (Específica a Mapas)

    Prevenir Rebuilds de MapWidget

    class MapScreen extends StatefulWidget {
      @override
      Widget build(BuildContext context) {
        return RepaintBoundary(
          child: MapboxMap(
            // ... configuración
          ),
        );
      }
    }
    

    Gestión de Streams

    StreamSubscription? _locationSubscription;
    StreamSubscription? _compassSubscription;
    
    @override
    void dispose() {
      _locationSubscription?.cancel();
      _compassSubscription?.cancel();
      _mapController?.dispose();
      super.dispose();
    }
    

    Actualización Eficiente de Markers

    • Usar updateAnnotation en lugar de recrear
    • Batch updates cuando sea posible
    • Remover markers fuera de viewport

    Integración con Business Logic

    Separación de Responsabilidades

    • Map Skill: Cómo mostrar el mapa, markers, rutas
    • Business Logic: Qué paquetes mostrar, cuándo actualizar estado

    Ejemplo de Integración

    // Map Provider (Presentation Layer)
    class MapProvider extends Notifier<MapState> {
      void showPackagesOnMap(List<Package> packages) {
        final annotations = packages.map((p) => 
          _createPackageMarker(p)
        ).toList();
        
        _mapController.addAnnotations(annotations);
      }
      
      PointAnnotation _createPackageMarker(Package package) {
        // Lógica de visualización
      }
    }
    

    Checklist de Implementación

    Antes de considerar feature de mapa completa:

    • Offline maps configurado para Fusagasugá
    • Location tracking con threshold de 1m/4°
    • LERP smoothing implementado para rotación
    • Polyline gradient para progreso de ruta
    • Markers numerados para paquetes
    • Caché local de geocoding (30 días)
    • Smart debouncing en búsquedas (500ms)
    • Bounding box estricto aplicado
    • Lazy geocoding implementado
    • Coordinate compression (6 decimales)
    • Fallback offline funcional
    • RepaintBoundary en MapWidget
    • Streams cancelados en dispose
    Recommended Servers
    Google Maps
    Google Maps
    Yandex
    Yandex
    ThinAir Geo
    ThinAir Geo
    Repository
    f4tb0yz/vector
    Files