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 componentfrontend/src/components/admin/VectorMapStyleSelector.tsx- Admin UI for vector mapsVECTOR_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
-
Get a free API key from MapTiler
- Sign up (free tier: 100,000 tile requests/month)
- Copy your API key
-
Add to environment:
Create/edit .env file:
REACT_APP_MAPTILER_KEY=your_api_key_here
- 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
- Clone the repository:
git clone https://github.com/openmaptiles/positron-gl-style.git
-
Customize
style.json -
Host on your server or CDN
-
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:
- API Key Issue:
Error: HTTP 403 - Unauthorized
Solution: Add valid REACT_APP_MAPTILER_KEY to .env
- Network Error:
Error: Failed to fetch tiles
Solution: Check internet connection, firewall settings
- 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
clubPrimaryColoris 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:
- Multiple Markers - Show multiple club locations
- Custom Marker Icons - Use club logo as marker
- Heatmaps - Show fan distribution
- 3D Buildings - Add depth to maps
- Route Planning - Show directions to stadium
- Catchment Areas - Draw radius around stadium
- Animation - Animate markers and transitions
- Clustering - Group nearby markers
📚 Resources
Documentation
Tools
- Maputnik Style Editor - Visual style editor
- MapTiler Cloud - Hosted tiles
- OpenMapTiles - Self-hosted solution
Examples
✅ Checklist for Production
- Obtain MapTiler API key
- Add
REACT_APP_MAPTILER_KEYto.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
Phase 1: Parallel Run (Recommended)
- 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