shashwatak / satellite-js

Modular set of functions for SGP4 and SDP4 propagation of TLEs.
MIT License
911 stars 145 forks source link

Cannot read properties of undefined (reading 'twoline2satrec') #121

Closed MichaelBonnet closed 1 year ago

MichaelBonnet commented 1 year ago

Hello,

I am trying to modify @/vasturiano's React globe with satellites example into just a React component. In attempting to do so, I get Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'twoline2satrec')

See the issue for yourself at this codesandbox link.

I'm a little confused, as mapping with [name, ...tle] and then passing ...tle to twoline2satrec() should be giving it the two TLE line strings as expected. What might I be doing wrong here? And why does this issue not appear in the example link's implementation?

Actual direct code:

import React, { useEffect, useState, useRef, useMemo } from "react";
import satellite from "satellite.js";
import Globe from "react-globe.gl";
import * as THREE from "three";

export default function App() {
  const EARTH_RADIUS_KM = 6371; // km
  const SAT_SIZE = 80; // km
  const TIME_STEP = 3 * 1000; // per frame

  const globeEl = useRef();
  const [satData, setSatData] = useState();
  const [globeRadius, setGlobeRadius] = 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("//unpkg.com/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)
          .slice(0, 1500);

        setSatData(satData);
      });
  }, []);

  const objectsData = 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.degreesLat(gdPos.latitude);
        const lng = satellite.degreesLong(gdPos.longitude);
        const alt = gdPos.height / EARTH_RADIUS_KM;
        return { ...d, lat, lng, alt };
      }
      return d;
    });
  }, [satData, time]);

  const satObject = useMemo(() => {
    if (!globeRadius) return undefined;

    const satGeometry = new THREE.OctahedronGeometry(
      (SAT_SIZE * globeRadius) / EARTH_RADIUS_KM / 2,
      0
    );
    const satMaterial = new THREE.MeshLambertMaterial({
      color: "palegreen",
      transparent: true,
      opacity: 0.7
    });
    return new THREE.Mesh(satGeometry, satMaterial);
  }, [globeRadius]);

  useEffect(() => {
    setGlobeRadius(globeEl.current.getGlobeRadius());
    globeEl.current.pointOfView({ altitude: 3.5 });
  }, []);

  return (
    <div>
      <Globe
        ref={globeEl}
        globeImageUrl="//unpkg.com/three-globe/example/img/earth-blue-marble.jpg"
        objectsData={objectsData}
        objectLabel="name"
        objectLat="lat"
        objectLng="lng"
        objectAltitude="alt"
        objectFacesSurfaces={false}
        objectThreeObject={satObject}
      />
      ,
    </div>
  );
}
MichaelBonnet commented 1 year ago

of note, @/vasturiano's example does have a live working version, which does not seem to be having this issue.

Indeed, directly passing two strings rather than using the mapped ...tle does not solve the problem:

const satData = tleData
          .map(([name, ...tle]) => ({
            satrec: satellite.twoline2satrec(
              "1    11U 59001A   22053.83197560  .00000847  00000-0  45179-3 0  9996",
              "2    11  32.8647 264.6509 1466352 126.0358 248.5175 11.85932318689790"
            ),
            name: name.trim().replace(/^0 /, ""),
          }))
MichaelBonnet commented 1 year ago

Update: the issue was to use the following import line:

import * as satellite from "satellite.js";

I hope someone else finds this issue useful.