// Mapbox Globe Visualization - Global Population Distribution // Demonstrates: Globe projection, fog/atmosphere, data-driven styling, interactive popups // Note: Replace with your own Mapbox access token // Get a free token at https://account.mapbox.com/ mapboxgl.accessToken = 'pk.eyJ1IjoibGludXhpc2Nvb2wiLCJhIjoiY2w3ajM1MnliMDV4NDNvb2J5c3V5dzRxZyJ9.wJukH5hVSiO74GM_VSJR3Q'; // Initialize map with globe projection const map = new mapboxgl.Map({ container: 'map', // Use satellite style for optimal globe visualization style: 'mapbox://styles/mapbox/satellite-streets-v12', projection: 'globe', // Enable 3D globe projection zoom: 1.5, center: [30, 20], // Center on Africa/Europe pitch: 0 }); // Track rotation state let isRotating = true; let userInteracted = false; // Add navigation controls and fullscreen map.addControl(new mapboxgl.NavigationControl(), 'top-left'); map.addControl(new mapboxgl.FullscreenControl(), 'top-left'); // Configure atmosphere and fog for realistic globe appearance map.on('style.load', () => { // Enable fog/atmosphere with default settings for realistic globe effect map.setFog({ color: 'rgb(186, 210, 235)', // Light blue atmosphere 'high-color': 'rgb(36, 92, 223)', // Upper atmosphere color 'horizon-blend': 0.02, // Smooth transition at horizon 'space-color': 'rgb(11, 11, 25)', // Dark space color 'star-intensity': 0.6 // Subtle star field }); }); // Add population data layer when map loads map.on('load', () => { // Add the population data source map.addSource('population', { type: 'geojson', data: populationData }); // Add circle layer with data-driven styling map.addLayer({ id: 'population-circles', type: 'circle', source: 'population', paint: { // Size circles based on population using interpolation 'circle-radius': [ 'interpolate', ['linear'], ['get', 'population'], 1000000, 4, // 1M people = 4px radius 5000000, 8, // 5M people = 8px radius 10000000, 12, // 10M people = 12px radius 20000000, 18, // 20M people = 18px radius 30000000, 24 // 30M+ people = 24px radius ], // Color based on population density 'circle-color': [ 'interpolate', ['linear'], ['get', 'population'], 1000000, '#FFA500', // Orange for smaller cities 10000000, '#FF6347', // Tomato for medium cities 20000000, '#FF4500', // Orange-red for large cities 30000000, '#DC143C' // Crimson for mega-cities ], 'circle-opacity': 0.85, 'circle-stroke-width': 1, 'circle-stroke-color': '#ffffff', 'circle-stroke-opacity': 0.6 } }); // Update city count const cityCount = populationData.features.length; document.getElementById('city-count').textContent = cityCount; // Add popup on hover const popup = new mapboxgl.Popup({ closeButton: false, closeOnClick: false, offset: 10 }); map.on('mouseenter', 'population-circles', (e) => { map.getCanvas().style.cursor = 'pointer'; const coordinates = e.features[0].geometry.coordinates.slice(); const props = e.features[0].properties; // Format population with commas const formattedPop = props.population.toLocaleString(); const html = `
${props.country}
Population: ${formattedPop}
Year: ${props.year}
`; popup.setLngLat(coordinates) .setHTML(html) .addTo(map); }); map.on('mouseleave', 'population-circles', () => { map.getCanvas().style.cursor = ''; popup.remove(); }); // Start gentle auto-rotation startRotation(); }); // Rotation function for gentle globe spinning function startRotation() { if (!isRotating || userInteracted) return; // Rotate the globe slowly const center = map.getCenter(); center.lng += 0.5; // Adjust speed here (degrees per frame) map.easeTo({ center: center, duration: 1000, easing: (t) => t // Linear easing for smooth rotation }); setTimeout(() => { requestAnimationFrame(startRotation); }, 1000); } // Pause rotation on user interaction map.on('mousedown', () => { userInteracted = true; isRotating = false; }); // Resume rotation after inactivity let inactivityTimer; map.on('mouseup', () => { clearTimeout(inactivityTimer); inactivityTimer = setTimeout(() => { if (map.getZoom() < 3) { // Only rotate at globe zoom levels userInteracted = false; isRotating = true; startRotation(); } }, 3000); // Resume after 3 seconds of inactivity }); // Pause rotation when zoomed in map.on('zoom', () => { if (map.getZoom() >= 3) { isRotating = false; } else if (!userInteracted) { isRotating = true; } }); // Handle touch events for mobile map.on('touchstart', () => { userInteracted = true; isRotating = false; }); // Log when map is ready map.on('idle', () => { if (map.loaded()) { console.log('Globe visualization loaded successfully'); console.log(`Displaying ${populationData.features.length} cities`); } });