6.2 KiB
Maps V2 Setup Guide
Installation
1. Install Dependencies
Add MapLibre GL JS to your package.json:
npm install maplibre-gl@^4.0.0
# or
yarn add maplibre-gl@^4.0.0
2. Configure Routes
Add the Map V2 route to config/routes.rb:
# Map V2 - Modern mobile-first implementation
get 'map/v2', to: 'map_v2#index', as: :map_v2
3. Register Stimulus Controller
The controller should auto-register if using Stimulus autoloading. If not, add to app/javascript/controllers/index.js:
import MapV2Controller from "./map_v2_controller"
application.register("map-v2", MapV2Controller)
4. Add MapLibre CSS
The view template already includes the MapLibre CSS CDN link. For production, consider adding it to your asset pipeline:
<link href="https://unpkg.com/maplibre-gl@4.0.0/dist/maplibre-gl.css" rel="stylesheet">
Or via npm/importmap:
import 'maplibre-gl/dist/maplibre-gl.css'
Usage
Basic Usage
Visit /map/v2 in your browser to see the new map interface.
URL Parameters
The map supports the same URL parameters as V1:
start_at- Start date/time (ISO 8601 format)end_at- End date/time (ISO 8601 format)tracks_debug=true- Show tracks/routes (experimental)
Example:
/map/v2?start_at=2024-01-01T00:00&end_at=2024-01-31T23:59
Features
Mobile Features
- Bottom Sheet: Swipe up/down to access layer controls
- Gesture Controls:
- Pinch to zoom
- Two-finger drag to pan
- Long press for context actions
- Touch-Optimized: Large buttons and controls
- Responsive: Adapts to screen size and orientation
Desktop Features
- Sidebar: Persistent controls panel
- Keyboard Shortcuts: (Coming soon)
- Multi-panel Layout: (Coming soon)
Architecture
Core Components
-
MapEngine (
core/MapEngine.js)- MapLibre GL JS wrapper
- Handles map initialization and basic operations
- Manages sources and layers
-
StateManager (
core/StateManager.js)- Centralized state management
- Persistent storage
- Reactive updates
-
EventBus (
core/EventBus.js)- Component communication
- Pub/sub system
- Decoupled architecture
-
LayerManager (
layers/LayerManager.js)- Layer lifecycle management
- GeoJSON conversion
- Click handlers and popups
-
BottomSheet (
components/BottomSheet.js)- Mobile-first UI component
- Gesture-based interaction
- Snap points support
Data Flow
User Action
↓
Stimulus Controller
↓
State Manager (updates state)
↓
Event Bus (emits events)
↓
Components (react to events)
↓
Map Engine (updates map)
Customization
Adding Custom Layers
// In your controller or component
this.layerManager.registerLayer('custom-layer', {
name: 'My Custom Layer',
type: 'circle',
source: 'custom-source',
paint: {
'circle-radius': 6,
'circle-color': '#ff0000'
}
})
// Add the layer
this.layerManager.addCustomLayer(customData)
Changing Theme
// Programmatically change theme
this.mapEngine.setStyle('dark') // or 'light'
// Via state manager
this.stateManager.set('ui.theme', 'dark')
Custom Bottom Sheet Content
import { BottomSheet } from '../maps_v2/components/BottomSheet'
const customContent = document.createElement('div')
customContent.innerHTML = '<h2>Custom Content</h2>'
const sheet = new BottomSheet({
content: customContent,
snapPoints: [0.1, 0.5, 0.9],
initialSnap: 0.5
})
Performance Optimization
Point Clustering
Points are automatically clustered at lower zoom levels to improve performance:
// Clustering is enabled by default for points
// Adjust cluster settings:
this.mapEngine.addSource('points-source', geojson, {
cluster: true,
clusterMaxZoom: 14, // Max zoom to cluster points
clusterRadius: 50 // Radius of cluster in pixels
})
Layer Visibility
Only load layers when needed:
// Lazy load heatmap
eventBus.on(Events.LAYER_ADD, (data) => {
if (data.layerId === 'heatmap') {
this.layerManager.addHeatmapLayer()
}
})
Debugging
Enable Debug Mode
// In browser console
localStorage.setItem('mapV2Debug', 'true')
location.reload()
Event Logging
// Log all events
eventBus.on('*', (event, data) => {
console.log(`[Event] ${event}:`, data)
})
State Inspector
// In browser console
console.log(this.stateManager.export())
Troubleshooting
Map Not Loading
- Check browser console for errors
- Verify MapLibre GL JS is loaded:
console.log(maplibregl) - Check if container element exists:
document.querySelector('[data-controller="map-v2"]')
Bottom Sheet Not Working
- Ensure touch events are not prevented by other elements
- Check z-index of bottom sheet (should be 999)
- Verify snap points are between 0 and 1
Performance Issues
- Reduce point count with clustering
- Limit date range to reduce data
- Disable unused layers
- Use simplified rendering mode
Migration from V1
Differences from V1
| Feature | V1 (Leaflet) | V2 (MapLibre) |
|---|---|---|
| Base Library | Leaflet.js | MapLibre GL JS |
| Rendering | Canvas | WebGL |
| Mobile UI | Basic | Bottom Sheet |
| State Management | None | Centralized |
| Event System | Direct calls | Event Bus |
| Layer Management | Manual | Managed |
Compatibility
V2 is designed to coexist with V1. Both can be used simultaneously:
- V1:
/map - V2:
/map/v2
Data Format
Both versions use the same backend API and data format, making migration straightforward.
Browser Support
- ✅ Chrome 90+
- ✅ Firefox 88+
- ✅ Safari 14+
- ✅ Edge 90+
- ✅ iOS Safari 14+
- ✅ Chrome Mobile 90+
WebGL required for MapLibre GL JS.
Contributing
Code Style
- Use ES6+ features
- Follow existing patterns
- Add JSDoc comments
- Keep components focused
Testing
# Run tests (when available)
npm test
# Lint code
npm run lint