Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>Globe</title> | |
| <style> | |
| body { margin: 0; overflow: hidden; } | |
| canvas { display: block; } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Three.js core --> | |
| <script src="https://unpkg.com/three@0.128.0/build/three.min.js"></script> | |
| <!-- OrbitControls for mouse interaction --> | |
| <script src="https://unpkg.com/three@0.128.0/examples/js/controls/OrbitControls.js"></script> | |
| <script> | |
| // 1) Boilerplate: scene, camera, renderer | |
| const scene = new THREE.Scene(); | |
| scene.background = new THREE.Color(0x000011); | |
| const camera = new THREE.PerspectiveCamera( | |
| 50, window.innerWidth / window.innerHeight, 1, 2000 | |
| ); | |
| camera.position.set(0, 0, 400); | |
| const renderer = new THREE.WebGLRenderer({ antialias: true }); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| document.body.appendChild(renderer.domElement); | |
| // 2) OrbitControls: drag to rotate, scroll to zoom | |
| const controls = new THREE.OrbitControls(camera, renderer.domElement); | |
| controls.enableDamping = true; | |
| controls.dampingFactor = 0.1; | |
| controls.enablePan = false; | |
| controls.minDistance = 250; | |
| controls.maxDistance = 800; | |
| controls.rotateSpeed = 0.6; | |
| controls.zoomSpeed = 1.2; | |
| // 3) Container for all country wires | |
| const globe = new THREE.Group(); | |
| scene.add(globe); | |
| // 4) Load GeoJSON and convert each polygon into a THREE.Line | |
| fetch('https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson') | |
| .then(r => r.json()) | |
| .then(geojson => { | |
| const R = 200; // globe radius | |
| const mat = new THREE.LineBasicMaterial({ color: 0xffffff, linewidth: 1 }); | |
| geojson.features.forEach(f => { | |
| let polys = []; | |
| if (f.geometry.type === 'Polygon') { | |
| polys = f.geometry.coordinates; | |
| } else if (f.geometry.type === 'MultiPolygon') { | |
| // flatten one level | |
| polys = f.geometry.coordinates.flat(); | |
| } | |
| polys.forEach(poly => { | |
| const pts = poly.map(([lon, lat]) => { | |
| // Map lon, lat → spherical coords | |
| // const phi = (90 - lat) * Math.PI / 180; | |
| // const theta = (lon + 180) * Math.PI / 180; | |
| const phi = -1 * (lat - 90) * (Math.PI / 180); | |
| const theta = lon * (Math.PI / 180); | |
| return new THREE.Vector3( | |
| R * Math.sin(phi) * Math.cos(theta), | |
| R * Math.cos(phi), | |
| R * Math.sin(phi) * Math.sin(theta) | |
| ); | |
| }); | |
| // close the ring | |
| if (pts.length > 0) pts.push(pts[0].clone()); | |
| const geo = new THREE.BufferGeometry().setFromPoints(pts); | |
| globe.add(new THREE.Line(geo, mat)); | |
| }); | |
| }); | |
| }) | |
| .catch(err => console.error('GeoJSON load error:', err)); | |
| // 5) Handle resize | |
| window.addEventListener('resize', () => { | |
| camera.aspect = window.innerWidth / window.innerHeight; | |
| camera.updateProjectionMatrix(); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| }); | |
| // 6) Render loop | |
| (function animate() { | |
| requestAnimationFrame(animate); | |
| controls.update(); | |
| renderer.render(scene, camera); | |
| })(); | |
| </script> | |
| </body> | |
| </html> |