Files
MyClub/DOCS/VECTOR_MAPS_IMPLEMENTATION.md
T
Tomáš Dvořák 12cba639b9 upload
2025-10-16 13:32:05 +02:00

11 KiB

Vector Maps Implementation with MapLibre GL JS & OpenMapTiles

🚀 Overview

This implementation uses MapLibre GL JS (open-source) with OpenMapTiles vector tiles to provide:

  • Better performance - Vector tiles are smaller and faster
  • Sharp rendering - Perfect on Retina/HiDPI displays
  • Dynamic styling - Change colors and styles at runtime
  • Club color integration - Automatic application of club colors to map elements
  • Custom style support - Full control via Mapbox GL Style JSON

📦 What Was Added

New Files

  • frontend/src/components/home/VectorMap.tsx - Main vector map component
  • frontend/src/components/admin/VectorMapStyleSelector.tsx - Admin UI for vector maps
  • VECTOR_MAPS_IMPLEMENTATION.md - This documentation

Dependencies Added

{
  "maplibre-gl": "^4.x.x",
  "@types/maplibre-gl": "^4.x.x"
}

🎨 Features

1. Vector Map Styles

Four professional styles from MapTiler (based on OpenMapTiles):

Style Description Best For
Positron Clean light style Data visualization, colored overlays
Dark Matter Sleek dark theme Modern interfaces, night mode
OSM Bright Colorful OSM style General purpose, vibrant look
Basic Simple clean base Minimal, professional

2. Dynamic Club Color Integration

The vector map automatically applies your club colors to:

  • Marker pins - Colored with primary club color
  • Water features - Tinted with club color (subtle)
  • Custom elements - Can be extended via style JSON

3. Custom Style Support

Advanced users can provide custom Mapbox GL Style JSON:

<VectorMap 
  customStyleUrl="https://your-server.com/custom-style.json"
  clubPrimaryColor="#e11d48"
  // ... other props
/>

🛠️ Technical Details

MapLibre GL JS

Why MapLibre?

  • Open-source fork of Mapbox GL JS
  • No vendor lock-in
  • Free for all uses
  • Active community development
  • Compatible with Mapbox styles

OpenMapTiles

Data Source:

  • Vector tiles from MapTiler API (free tier available)
  • Based on OpenStreetMap data
  • Updated regularly
  • Global coverage

Performance:

  • Vector tiles are ~75% smaller than raster tiles
  • Smooth zooming and rotation
  • Client-side rendering
  • GPU acceleration

📋 Usage

Basic Usage

import VectorMap from '../components/home/VectorMap';

<VectorMap
  latitude={50.0755}
  longitude={14.4378}
  zoom={15}
  clubName="FK Example"
  address="Example Street 123, Prague"
  mapStyle="positron"
  clubPrimaryColor="#e11d48"
  clubSecondaryColor="#3b82f6"
/>

Props

interface VectorMapProps {
  latitude: number;                    // Required
  longitude: number;                   // Required
  zoom?: number;                       // Default: 15
  address?: string;                    // Optional popup content
  clubName?: string;                   // Optional popup content
  mapStyle?: 'positron' | 'dark-matter' | 'osm-bright' | 'klokantech-basic';
  height?: number;                     // Default: 400
  clubPrimaryColor?: string;           // Hex color for marker
  clubSecondaryColor?: string;         // Secondary color
  customStyleUrl?: string;             // Custom Mapbox GL Style JSON URL
}

🔑 API Key Setup

For Development

The component includes a demo API key for testing.

For Production

  1. Get a free API key from MapTiler

    • Sign up (free tier: 100,000 tile requests/month)
    • Copy your API key
  2. Add to environment:

Create/edit .env file:

REACT_APP_MAPTILER_KEY=your_api_key_here
  1. Rebuild the app:
npm run build

🎨 Custom Styling

Method 1: Modify Built-in Styles

Edit VectorMap.tsx and customize the createCustomPositronStyle function:

const createCustomPositronStyle = (primaryColor?: string, secondaryColor?: string): any => {
  return {
    version: 8,
    name: 'Custom Style',
    sources: { /* ... */ },
    layers: [
      // Add/modify layers here
      {
        id: 'water',
        type: 'fill',
        paint: { 
          'fill-color': yourCustomColor 
        }
      }
    ]
  };
};

Method 2: External Style JSON

Host your custom style JSON and reference it:

<VectorMap 
  customStyleUrl="https://your-cdn.com/custom-positron.json"
/>

Style JSON structure (based on Mapbox GL Style Spec):

{
  "version": 8,
  "name": "Custom Style",
  "sources": {
    "openmaptiles": {
      "type": "vector",
      "url": "https://api.maptiler.com/tiles/v3/tiles.json?key=YOUR_KEY"
    }
  },
  "layers": [
    {
      "id": "background",
      "type": "background",
      "paint": { "background-color": "#f8f8f8" }
    },
    {
      "id": "water",
      "type": "fill",
      "source": "openmaptiles",
      "source-layer": "water",
      "paint": { "fill-color": "#your-color" }
    }
    // ... more layers
  ]
}

Method 3: Fork Positron GL Style

  1. Clone the repository:
git clone https://github.com/openmaptiles/positron-gl-style.git
  1. Customize style.json

  2. Host on your server or CDN

  3. Reference in your app

🎯 Integration with Existing System

Option 1: Replace Existing Maps

Update imports in your components:

// Old
import ContactMap from '../components/home/ContactMap';

// New
import VectorMap from '../components/home/VectorMap';

// Usage stays similar
<VectorMap 
  latitude={lat}
  longitude={lng}
  clubPrimaryColor={settings.primary_color}
  // ... same props
/>

Option 2: Toggle Between Raster/Vector

Create a wrapper component:

const AdaptiveMap: React.FC<MapProps> = (props) => {
  const useVector = settings.use_vector_maps; // from settings
  
  return useVector 
    ? <VectorMap {...props} />
    : <ContactMap {...props} />;
};

📊 Performance Comparison

Vector Maps (MapLibre GL)

  • Initial Load: 150-200 KB (tiles + library)
  • Per Zoom: 20-50 KB (vector tiles)
  • Rendering: GPU-accelerated
  • Zoom Quality: Always sharp
  • Customization: Full runtime control

Raster Maps (Leaflet)

  • Initial Load: 100 KB (library only)
  • Per Zoom: 100-200 KB (PNG tiles)
  • Rendering: Image-based
  • Zoom Quality: Pixelated between levels
  • Customization: Limited to tile source

Verdict: Vector maps are faster after initial load and provide better visual quality.

🌍 Self-Hosting Tiles (Advanced)

For complete independence from third-party services:

1. Set Up OpenMapTiles Server

# Clone OpenMapTiles
git clone https://github.com/openmaptiles/openmaptiles.git
cd openmaptiles

# Generate tiles for your region
./quickstart.sh czech-republic

# Start tile server
docker-compose up -d

2. Update VectorMap Component

const TILE_SERVER = 'http://your-server.com';

sources: {
  'openmaptiles': {
    type: 'vector',
    url: `${TILE_SERVER}/tiles.json`
  }
}

Benefits of Self-Hosting:

  • No API key required
  • No request limits
  • Full data control
  • Offline capability
  • Requires server maintenance

🎨 Club Color Examples

Red Club (e.g., Slavia Prague)

<VectorMap 
  mapStyle="positron"
  clubPrimaryColor="#e11d48"
  clubSecondaryColor="#ffffff"
/>

Result: Red markers on light map, subtle red tint on water

Blue Club (e.g., Slovan Liberec)

<VectorMap 
  mapStyle="dark-matter"
  clubPrimaryColor="#3b82f6"
  clubSecondaryColor="#60a5fa"
/>

Result: Blue markers on dark map, modern look

Green Club

<VectorMap 
  mapStyle="osm-bright"
  clubPrimaryColor="#16a34a"
  clubSecondaryColor="#fbbf24"
/>

Result: Green markers on colorful map

🐛 Troubleshooting

Map Not Loading

Check browser console for errors:

  1. API Key Issue:
Error: HTTP 403 - Unauthorized

Solution: Add valid REACT_APP_MAPTILER_KEY to .env

  1. Network Error:
Error: Failed to fetch tiles

Solution: Check internet connection, firewall settings

  1. Style Loading Error:
Error: Failed to parse style

Solution: Validate your custom style JSON

Blank Map

  • Verify coordinates are valid (lat: -90 to 90, lng: -180 to 180)
  • Check zoom level (1-20)
  • Ensure API key has not exceeded quota

Marker Not Visible

  • Check clubPrimaryColor is a valid color
  • Ensure marker color contrasts with map style
  • Verify marker is within viewport

Performance Issues

  • Reduce zoom level for overview maps
  • Use simpler map style (e.g., 'klokantech-basic')
  • Limit number of map instances per page

📱 Mobile Optimization

Vector maps are automatically mobile-optimized:

  • Touch gestures (pinch, pan, rotate)
  • Responsive sizing
  • Efficient data loading
  • GPU acceleration on mobile

Tips:

  • Use lower zoom levels on mobile (12-14 instead of 15-17)
  • Consider disabling rotation on mobile
  • Test on actual devices, not just desktop browser

🔮 Future Enhancements

Potential additions:

  1. Multiple Markers - Show multiple club locations
  2. Custom Marker Icons - Use club logo as marker
  3. Heatmaps - Show fan distribution
  4. 3D Buildings - Add depth to maps
  5. Route Planning - Show directions to stadium
  6. Catchment Areas - Draw radius around stadium
  7. Animation - Animate markers and transitions
  8. Clustering - Group nearby markers

📚 Resources

Documentation

Tools

Examples

Checklist for Production

  • Obtain MapTiler API key
  • Add REACT_APP_MAPTILER_KEY to .env
  • Test all map styles
  • Verify club colors are applied correctly
  • Test on mobile devices
  • Check performance with browser DevTools
  • Monitor API usage in MapTiler dashboard
  • Consider self-hosting for high-traffic sites
  • Add error boundaries for graceful failures
  • Document custom styles if used

🎯 Migration Path

  • Keep existing Leaflet maps
  • Add vector maps as opt-in feature
  • A/B test with users
  • Monitor performance and feedback

Phase 2: Gradual Rollout

  • Enable vector maps for new features
  • Offer toggle in admin settings
  • Migrate high-traffic pages first

Phase 3: Full Migration

  • Default to vector maps
  • Keep raster maps as fallback
  • Remove Leaflet dependency

Implementation Date: 2025-10-10
Technology: MapLibre GL JS 4.x + OpenMapTiles
License: Open Source (BSD-3-Clause)
Status: Ready for Integration