Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
| import mapboxgl from 'mapbox-gl'; | |
| import { MAPBOX_TOKEN, MAP_CONFIG, HOME_BASE, COLORS } from '../config.js'; | |
| import { FlightPathLayer } from '../webgl/FlightPathLayer.js'; | |
| export class MapView { | |
| constructor(container, options = {}) { | |
| this.container = container; | |
| this.markers = new window.Map(); | |
| this.homeMarker = null; | |
| this.onEventSelect = options.onEventSelect || (() => {}); | |
| this.flightPathLayer = null; | |
| this.init(); | |
| } | |
| init() { | |
| mapboxgl.accessToken = MAPBOX_TOKEN; | |
| this.map = new mapboxgl.Map({ | |
| container: this.container, | |
| ...MAP_CONFIG | |
| }); | |
| this.map.on('load', () => { | |
| this.addHomeBase(); | |
| this.flightPathLayer = new FlightPathLayer(this.map); | |
| this.map.resize(); | |
| }); | |
| this.map.addControl(new mapboxgl.NavigationControl(), 'top-left'); | |
| } | |
| addHomeBase() { | |
| const el = document.createElement('div'); | |
| el.className = 'home-marker'; | |
| el.innerHTML = ` | |
| <svg width="24" height="24" viewBox="0 0 24 24" fill="none"> | |
| <circle cx="12" cy="12" r="10" fill="${COLORS.blue}" stroke="${COLORS.white}" stroke-width="2"/> | |
| <path d="M12 6L16 14H8L12 6Z" fill="${COLORS.white}"/> | |
| </svg> | |
| `; | |
| this.homeMarker = new mapboxgl.Marker({ element: el, anchor: 'center' }) | |
| .setLngLat(HOME_BASE.coordinates) | |
| .addTo(this.map); | |
| } | |
| setEvents(events) { | |
| this.events = events; | |
| // Clear existing markers | |
| this.markers.forEach(marker => marker.remove()); | |
| this.markers.clear(); | |
| events.forEach(event => { | |
| const el = this.createMarkerElement(event); | |
| const marker = new mapboxgl.Marker({ element: el, anchor: 'center' }) | |
| .setLngLat(event.location.coordinates) | |
| .addTo(this.map); | |
| el.addEventListener('click', () => { | |
| this.onEventSelect(event); | |
| }); | |
| this.markers.set(event.id, marker); | |
| }); | |
| } | |
| createMarkerElement(event) { | |
| const el = document.createElement('div'); | |
| el.className = `event-marker ${event.type}`; | |
| const color = event.type === 'international' ? COLORS.blue : COLORS.red; | |
| el.innerHTML = ` | |
| <svg width="20" height="20" viewBox="0 0 20 20" fill="none"> | |
| <circle cx="10" cy="10" r="8" fill="${color}" opacity="0.2"/> | |
| <circle cx="10" cy="10" r="5" fill="${color}"/> | |
| </svg> | |
| `; | |
| return el; | |
| } | |
| flyTo(coordinates, zoom = 9) { | |
| this.map.flyTo({ | |
| center: coordinates, | |
| zoom, | |
| duration: 1500, | |
| essential: true | |
| }); | |
| } | |
| selectEvent(eventId) { | |
| // Reset all markers | |
| this.markers.forEach((marker, id) => { | |
| marker.getElement().classList.remove('selected'); | |
| }); | |
| // Highlight selected marker | |
| const marker = this.markers.get(eventId); | |
| if (marker) { | |
| marker.getElement().classList.add('selected'); | |
| } | |
| } | |
| // Show full static path between two points | |
| showFlightPath(from, to) { | |
| if (this.flightPathLayer) { | |
| this.flightPathLayer.showPath(from, to); | |
| } | |
| } | |
| // Animate path being traced, call onComplete when done | |
| animateFlightPath(from, to, duration, onComplete) { | |
| if (this.flightPathLayer) { | |
| this.flightPathLayer.animatePath(from, to, duration, onComplete); | |
| } | |
| } | |
| hideFlightPath() { | |
| if (this.flightPathLayer) { | |
| this.flightPathLayer.hide(); | |
| } | |
| } | |
| resetView() { | |
| this.map.flyTo({ | |
| center: MAP_CONFIG.center, | |
| zoom: MAP_CONFIG.zoom, | |
| duration: 1000 | |
| }); | |
| this.hideFlightPath(); | |
| } | |
| zoomToFrance() { | |
| this.map.flyTo({ | |
| center: MAP_CONFIG.center, | |
| zoom: 4.3, | |
| duration: 800 | |
| }); | |
| } | |
| getMap() { | |
| return this.map; | |
| } | |
| } | |