import { economicData } from './data/economic-data.js'; // Mapbox access token mapboxgl.accessToken = 'pk.eyJ1IjoibGludXhpc2Nvb2wiLCJhIjoiY2w3ajM1MnliMDV4NDNvb2J5c3V5dzRxZyJ9.wJukH5hVSiO74GM_VSJR3Q'; // Initialize map with globe projection const map = new mapboxgl.Map({ container: 'map', style: 'mapbox://styles/mapbox/dark-v11', projection: 'globe', center: [20, 20], zoom: 1.5, attributionControl: false }); // Add attribution and navigation controls map.addControl(new mapboxgl.AttributionControl({ compact: true }), 'bottom-right'); map.addControl(new mapboxgl.NavigationControl(), 'top-right'); // Configuration for atmosphere map.on('style.load', () => { map.setFog({ color: 'rgb(186, 210, 235)', 'high-color': 'rgb(36, 92, 223)', 'horizon-blend': 0.02, 'space-color': 'rgb(11, 11, 25)', 'star-intensity': 0.6 }); }); // Active metric for visualization let activeMetric = 'gdpPerCapita'; let activeColorMetric = 'growthRate'; // Color scales and data ranges const colorScales = { growthRate: { // Diverging scale: negative (red) to zero (white) to positive (green) stops: [ [-25, '#b2182b'], // Deep red for severe contraction [-10, '#ef8a62'], // Light red for contraction [-5, '#fddbc7'], // Very light red [0, '#f7f7f7'], // White for zero growth [2, '#d1e5f0'], // Very light blue [4, '#67a9cf'], // Light blue for moderate growth [8, '#2166ac'] // Deep blue for strong growth ] }, developmentIndex: { // Sequential scale for development (low to high) stops: [ [0.35, '#fff7ec'], [0.45, '#fee8c8'], [0.55, '#fdd49e'], [0.65, '#fdbb84'], [0.75, '#fc8d59'], [0.85, '#ef6548'], [0.95, '#d7301f'] ] }, gdpPerCapita: { // Sequential scale for GDP (low to high) stops: [ [0, '#f7fcf5'], [5000, '#e5f5e0'], [10000, '#c7e9c0'], [20000, '#a1d99b'], [40000, '#74c476'], [60000, '#41ab5d'], [100000, '#238b45'] ] }, tradeVolume: { // Sequential scale for trade stops: [ [0, '#fff5f0'], [50, '#fee0d2'], [200, '#fcbba1'], [500, '#fc9272'], [1000, '#fb6a4a'], [3000, '#ef3b2c'], [6000, '#a50f15'] ] } }; // Size scales const sizeScales = { gdpPerCapita: { min: 3, max: 25, stops: [ [0, 3], [10000, 8], [30000, 15], [70000, 25] ] }, tradeVolume: { min: 3, max: 30, stops: [ [0, 3], [100, 8], [500, 15], [2000, 22], [6000, 30] ] }, developmentIndex: { min: 4, max: 20, stops: [ [0.35, 4], [0.55, 8], [0.75, 14], [0.95, 20] ] }, growthRate: { min: 5, max: 20, stops: [ [-25, 5], [-5, 8], [0, 10], [5, 15], [12, 20] ] } }; // Add economic data layer map.on('load', () => { // Add source map.addSource('economic-indicators', { type: 'geojson', data: economicData }); // Add circle layer with data-driven styling map.addLayer({ id: 'economic-circles', type: 'circle', source: 'economic-indicators', paint: { // Circle radius based on active size metric using interpolate expression 'circle-radius': [ 'interpolate', ['linear'], ['get', activeMetric], ...sizeScales[activeMetric].stops.flat() ], // Circle color based on growth rate using interpolate expression 'circle-color': [ 'interpolate', ['linear'], ['get', activeColorMetric], ...colorScales[activeColorMetric].stops.flat() ], // Opacity with zoom-based adjustment 'circle-opacity': [ 'interpolate', ['linear'], ['zoom'], 1, 0.7, 4, 0.8, 8, 0.9 ], // Stroke for better visibility 'circle-stroke-width': [ 'interpolate', ['linear'], ['zoom'], 1, 0.5, 4, 1, 8, 2 ], 'circle-stroke-color': '#ffffff', 'circle-stroke-opacity': 0.5 } }); // Add country labels layer map.addLayer({ id: 'country-labels', type: 'symbol', source: 'economic-indicators', layout: { 'text-field': ['get', 'code'], 'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'], 'text-size': [ 'interpolate', ['linear'], ['zoom'], 1, 8, 4, 12, 8, 16 ], 'text-offset': [0, 0], 'text-anchor': 'center' }, paint: { 'text-color': '#ffffff', 'text-halo-color': '#000000', 'text-halo-width': 1, 'text-opacity': [ 'interpolate', ['linear'], ['zoom'], 1, 0, 2, 0.6, 4, 1 ] }, minzoom: 1.5 }); // Create popup const popup = new mapboxgl.Popup({ closeButton: false, closeOnClick: false, offset: 10 }); // Show popup on hover map.on('mouseenter', 'economic-circles', (e) => { map.getCanvas().style.cursor = 'pointer'; const coordinates = e.features[0].geometry.coordinates.slice(); const props = e.features[0].properties; const html = `