人工衛星の位置をWeb地図上にリアルタイム表示する
FOSS4G Advent Calendar 2024 の 23 日目の記事です。
この記事では、人工衛星の位置を Web 地図上にリアルタイム表示する React コンポーネントライブラリ react-sat-map とその実装を紹介します。
デモ
はじめに、react-sat-map がどんなものかデモページをご覧ください。
以下はスクリーンショットのため動かないですが、実際のデモページでは衛星をクリックしたり、地図を拡大・縮小したりして、衛星の名前や実際の動きを確認できます。
デモで表示しているのは、直近 30 日以内に打上げられた衛星です。スクリーンショットで名前を表示している YODAKA (AE1b) は、私の所属するアークエッジ・スペースが開発した衛星で、12/9 に国際宇宙ステーションから放出されました(宣伝)。
YODAKA では、「短歌ミッション」の上の句を募集中です。
react-sat-map の使い方
react-sat-map のコンポーネントは、npm パッケージとして配布しています(この記事を書く直前に駆け込みでリリースしました)。
react-sat-map を使うには以下のパッケージも合わせて必要です。
react-sat-map のメインのコンポーネントは SatelliteMarkers
です。react-map-gl/maplibre
の Map
コンポーネントの小要素として使うことができます。satellites
prop に衛星の名前と TLE(このあと説明します)をもつオブジェクトの配列を渡してください。
以下に、YODAKA の位置を地図上に表示する例を示します。
import { Map } from "react-map-gl/maplibre";
import { SatelliteMarkers } from "react-sat-map";
import "maplibre-gl/dist/maplibre-gl.css";
import "react-sat-map/style.css";
const satellites = [
{
name: "YODAKA",
tle: {
line1:
"1 62295U 98067XB 24357.77132066 .00177505 00000+0 25824-2 0 9994",
line2:
"2 62295 51.6366 100.8197 0012760 9.6944 350.4290 15.54538666 2102",
},
},
];
export default function App() {
return (
<Map
style={{ height: "100dvh" }}
mapStyle="https://tile.openstreetmap.jp/styles/maptiler-basic-ja/style.json"
>
<SatelliteMarkers satellites={satellites} />
</Map>
);
}
ここからは、SatelliteMarkers
コンポーネントを実装していきます。
TLE から衛星の位置を計算する
まずは、衛星の現在位置を取得しましょう。
衛星の位置は 2 行軌道要素、TLE (Two-Line Elements) から計算できます。TLE は、人工衛星等の軌道を表すテキストフォーマットのデータで、CelesTrak や Space-Track(要アカウント)で配布されています。上記の例の line1
と line2
がそれぞれ TLE の 1 行目と 2 行目にあたります。
TLE の内容はここでは説明しません[2]が、衛星は TLE で表現される軌道上で運動を続けるので、SGP4 といったアルゴリズムによって、ある時刻にどこにいるかを求めることができます[3]。
satellite.js は、SGP4 の JavaScript 実装です。以下のようにして TLE と時刻から緯度経度を計算できます。
import * as satellite from "satellite.js";
function twoline2geodetic(line1, line2, date = new Date()) {
const satrec = satellite.twoline2satrec(line1, line2);
const { position } = satellite.propagate(satrec, date);
// 得られた position は ECI 座標系なので、緯度経度の測地座標系に変換する
const location = satellite.eciToGeodetic(position, satellite.gstime(date));
// ラジアンから度数法に変換する
const longitude = satellite.degreesLong(location.longitude);
const latitude = satellite.degreesLat(location.latitude);
return { longitude, latitude };
}
Web 地図上に衛星を表示する
衛星の位置が取得できたので、つぎは Web 地図上に表示します。
TLE と時刻を props として受け取り、react-map-gl/maplibre
の Marker
を返すコンポーネントをつくります。
import { Marker } from "react-map-gl/maplibre";
function SatelliteMarker({ satellite, date = new Date() }) {
const { longitude, latitude } = twoline2geodetic(
satellite.tle.line1,
satellite.tle.line2,
date,
);
return (
<Marker longitude={longitude} latitude={latitude}>
🛰️
</Marker>
);
}
このコンポーネントを react-map-gl/maplibre
の Map
コンポーネント以下に配置すれば、衛星が地図上に表示されます。
アニメーションでリアルタイムに位置を更新する
最後に、衛星の位置を現在時刻に合わせて更新し続けられるようにします。
SatelliteMarkers
コンポーネントでは、window.requestAnimationFrame()
を使用して、画面更新のたび SatelliteMarker
に新しい時刻を渡すようにしています。
export function SatelliteMarkers({ satellites }) {
const [date, setDate] = useState(new Date(performance.timeOrigin));
useEffect(() => {
const animation = requestAnimationFrame((timestamp) =>
setDate(new Date(performance.timeOrigin + timestamp));
);
return () => cancelAnimationFrame(animation);
});
return (
<>
{satellites.map((satellite, i) => (
<SatelliteMarker key={i} satellite={satellite} date={date} />
))}
</>
);
}
以上が react-sat-map のエッセンスです。実際のコードはパフォーマンスや安定性、ユーザビリティを考慮してもうすこし複雑になっています。具体的なコードは冒頭の GitHub リポジトリを参照してください。
現在の react-sat-map は衛星の位置をただ表示するだけですが、以下のような既存の OSS にはもっといろいろな機能があります。
ただ、2 次元の Web 地図上にシンプルに表示するものが見当たらなかったのでつくってみました。今後余力があれば、上記 OSS に共通するような主要な機能を追加していきたいです。
-
先日リリースされたばかりの React v19 が必要です。最新版でつくりはじめて、記事を書く直前に v18 で動作確認したら動きませんでした……。 ↩︎
-
TLE についてはつぎのページがわかりやすいです: https://lizard-tail.com/isana/tle/misc/what_is_tle ↩︎
-
実際には外乱により軌道は刻々と変化していくので、TLE は日々更新されます。 ↩︎
Discussion