UNPKG

r3f-globe

Version:

React Three Fiber component for Globe Data Visualization

116 lines (102 loc) 4.33 kB
<head> <style> body { margin: 0; } </style> <script type="importmap">{ "imports": { "three": "https://esm.sh/three", "three/": "https://esm.sh/three/", "three/addons/": "https://esm.sh/three/examples/jsm/", "react": "https://esm.sh/react", "react/": "https://esm.sh/react/", "react-dom": "https://esm.sh/react-dom/client", "react-dom/": "https://esm.sh/react-dom/", "@react-three/fiber": "https://esm.sh/@react-three/fiber?external=react,react-dom,three", "@react-three/drei": "https://esm.sh/@react-three/drei?external=@react-three/fiber,react,react-dom,three" }}</script> <!-- <script type="module">import * as THREE from 'three'; import * as React from 'react'; window.THREE = THREE; window.React = React;</script>--> <!-- <script src="../../dist/r3f-globe.js" defer></script>--> </head> <body> <div id="r3fScene"></div> <script src="//unpkg.com/@babel/standalone"></script> <script type="text/jsx" data-type="module"> import R3fGlobe from 'https://esm.sh/r3f-globe?external=three,react'; import React, { useMemo, useCallback, useEffect, useState } from "react"; import ReactDOM from "react-dom"; import { Canvas } from '@react-three/fiber'; import { OrbitControls } from '@react-three/drei'; import * as satellite from 'https://esm.sh/satellite.js'; const EARTH_RADIUS_KM = 6371; // km const TIME_STEP = 3 * 1000; // per frame const GlobeViz = () => { const [satData, setSatData] = useState(); const [time, setTime] = useState(new Date()); useEffect(() => { // time ticker (function frameTicker() { requestAnimationFrame(frameTicker); setTime(time => new Date(+time + TIME_STEP)); })(); }, []); useEffect(() => { // load satellite data fetch('//cdn.jsdelivr.net/npm/globe.gl/example/datasets/space-track-leo.txt').then(r => r.text()).then(rawData => { const tleData = rawData.replace(/\r/g, '') .split(/\n(?=[^12])/) .filter(d => d) .map(tle => tle.split('\n')); const satData = tleData.map(([name, ...tle]) => ({ satrec: satellite.twoline2satrec(...tle), name: name.trim().replace(/^0 /, '') })) // exclude those that can't be propagated .filter(d => !!satellite.propagate(d.satrec, new Date()).position); setSatData(satData); }); }, []); const particlesData = useMemo(() => { if (!satData) return []; // Update satellite positions const gmst = satellite.gstime(time); return [ satData.map(d => { const eci = satellite.propagate(d.satrec, time); if (eci.position) { const gdPos = satellite.eciToGeodetic(eci.position, gmst); const lat = satellite.radiansToDegrees(gdPos.latitude); const lng = satellite.radiansToDegrees(gdPos.longitude); const alt = gdPos.height / EARTH_RADIUS_KM; return { ...d, lat, lng, alt }; } return d; }).filter(d => !isNaN(d.lat) && !isNaN(d.lng) && !isNaN(d.alt)) ]; }, [satData, time]); return <R3fGlobe globeImageUrl="//cdn.jsdelivr.net/npm/three-globe/example/img/earth-blue-marble.jpg" particlesData={particlesData} particleLabel="name" particleLat="lat" particleLng="lng" particleAltitude="alt" particlesColor={useCallback(() => 'palegreen', [])} onHover={console.log} />; } const Scene = () => { return <div style={{ height: window.innerHeight }}> <Canvas flat camera={useMemo(() => ({ position: [0, 0, 250] }), [])} raycaster={useMemo(() => ({ params: { Points: { threshold: 0.2 } } }), [])} > <OrbitControls minDistance={101} maxDistance={1e4} dampingFactor={0.1} zoomSpeed={0.3} rotateSpeed={0.3} /> <color attach="background" args={[0, 0, 0]}/> <ambientLight intensity={Math.PI}/> <directionalLight intensity={0.6 * Math.PI}/> <GlobeViz /> </Canvas> </div>; }; ReactDOM.createRoot(document.getElementById('r3fScene')) .render(<Scene />); </script> </body>