20200121のReactに関する記事は4件です。

ページ遷移で、真っ白な画面が表示された

事象

Reactで作ったWebページをスマホで確認していたところ、とあるページ遷移で、真っ白な画面が表示された。
「ホワイトアウト」と呼ばれる現象。

処理内容

「componentDidMount」のタイミングで、Fetch APIを使ってデータ取得。
タイムアウト時間を過ぎたら、fetch処理をキャンセルさせようとした。

コード

fetch.js
// after render
componentDidMount(){

    const controller = new AbortController();

    const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
    // 2秒後にfetchキャンセル
    wait(2000).then(() => {
        controller.abort();
    });

    fetch('https://hogehoge.com/send', {
        mode: 'cors',
        method: 'POST',
        signal: controller.signal,  // controllerが持つsignalをfetchに渡す
        header: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify(JSONdata),
    }).then((data) => {
        // 応答が得られた場合の処理
    }).catch((error) => {
        // fetchキャンセルの場合、ここに入る
        // errorはundefined
    });
}

どこに問題があったか

「AbortController」をサポートしていないバージョンの「Chrome for Android」による読み込み。
この時の「Chrome for Android」のバージョンは「55」。
Chromeでの「AbortController」サポートは「66」以降になる。
https://developer.mozilla.org/en-US/docs/Web/API/AbortController

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React HooksのuseEffectの中でコンポーネント内の変数を呼び出す

Before

export const HogeComponent: React.FC = (() => {
  const fuga = useSelector((state: State) => state.fuga);
  const dispatch = useDispatch();

  useEffect(() => {
    const piyo = fuga.someMethod();
    dispatch(MogeAction.someAction(piyo));
  }, [dispatch]);

  return (......
});

このようなコンポーネントで、Appコンポーネントに付随する非同期処理で fuga がstoreに入る前に、 useEffect が呼ばれてしまった。

After

export const HogeComponent: React.FC = (
  const fuga = useSelector((state: State) => state.fuga);
  const dispatch = useDispatch();

  useEffect(() => {
    const piyo = fuga.someMethod();
    dispatch(MogeAction.someAction(piyo));
  }, [dispatch, fuga]); // 第2引数にfuga追加

  ......
});

useEffect の第2引数に使う変数を書いたところ、変数の更新と同時に useEffect が呼ばれるようになった。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GatsbyのHot reloadがMarkdownファイルをイジると死ぬ

どうしたの

GatsbyにはHot reload機能があって、普通にReactを書いているだけなら大丈夫なんですが、gatsby-source-filesystemとかでMarkdownをレンダリングしたい時に、Markdownを編集するとhot reloadが死にます。

GraphQLクエリが非同期で発行されるので、データが入っていない状態が存在することが原因ぽいです。開発中にいちいちhot reloadが死ぬと煩わしくてしょうがないのでこれを解決します。

解決

このIssueが参考になりました。

GraphQLのクエリを通してデータを引っ張ってくる部分がnullableであるという前提を置いてコードを書いていくと良さそうです。自分の場合はもともとこんな感じで書いていました。MDXを使っています。

post-template.tsx
export default ({ data } : Props) => {
  const { body, excerpt, headings, frontmatter } = data.mdx;

  return (
    <>
      <SEO />
      <StyledPost
        body={body}
        headings={headings}
      />
    </>
  );
};

これだとdata.mdxにデータが入る前にレンダリングされると死ぬので、以下のように一行追加しました。

post-template.tsx
export default ({ data } : Props) => {
+  if (!data.mdx) return <></>;
  const { body, excerpt, headings, frontmatter } = data.mdx;

  return (
    <>
      <SEO />
      <StyledPost
        body={body}
        headings={headings}
      />
    </>
  );
};

これでMarkdownファイルを編集しても、一瞬ホワイトアウトするくらいで、GraphQLクエリの実行結果が入ってきたら正常にレンダリングされるようになりました。

まとめ

二回目ですが,
GraphQLで引っ張ってくるデータは非同期処理が基本になっているので、nullableであることを前提においておくとよいですね。?
そもそも型定義からnullableにしておきましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

バーチャル地球儀上で国際宇宙ステーションの現在位置が分かるアプリの開発

アプリの紹介

今回開発したアプリ「Satellite Tracker」の紹介です。

Satellite Tracker

このアプリはChromeやSafariといったブラウザで閲覧できます。(WebGL対応のブラウザが必要です)。

satelite-tracker2.gif
「Satellite Tracker」は国際宇宙ステーション/International Space Station」(以下ISSと略)の現在位置と軌道について、バーチャル地球儀上で可視化してくれるアプリケーションです

ISSの位置は時刻と連動しており、時間の経過に合わせてISSが衛星軌道に沿って移動します。 また、カメラ追尾モードにすると、至近距離からISSが移動する様子も確認できます。

また、左下の時間操作UIを動かすことで経過時間を早めたり戻したりすることができます。
satelite-tracker-time-control.gif

3D表示も凄いのですが、2D地図の表示も可能です。
satelite-tracker-2D.gif

レスポンシブ対応がされており、スマートフォンでもISSの軌道を楽しめます。

以上でアプリケーションの紹介は終わりです。以降はこのアプリケーションを作ろうとした背景や、実装方法 についての説明です。興味のある方はどうぞ。

はじめに

この記事は、趣味で宇宙開発を行う団体「リーマンサット・プロジェクト」がお送りする新春アドベントカレンダー2020です。本記事は11日目です。

リーマンサット・プロジェクトは「普通の人が集まって宇宙開発しよう」を合言葉に活動をしている民間団体です。

他では経験できない「宇宙開発プロジェクト」に 誰もが携わることができます。
興味を持たれた方は https://www.rymansat.com/join からお気軽にどうぞ。

筆者について

自分はリーマンサットでは、2020年度打ち上げ予定である超小型人工衛星「RSP-01」の自撮り機能や、地上局システムのソフトウェア開発を担当しています。リーマンサットに関わり始めました時期は2019年の夏頃だったかと思います。

普段仕事ではWebサービスの開発を行っています。主にフロントエンド(React.js)、サーバーサイド(Ruby on Rails)の領域の開発を行っています。

そんな自分ですが、当然のことながら衛星開発の経験は全くありません。ただ、やる気と熱意さえあれば宇宙開発に関われるのがリーマンサットの特徴だと思います。

開発のきっかけ

リーマンサットでは2018年に超小型人工衛星「RSP-00」を打ち上げ、今年は現在開発中の人工衛星「RSP-01」の打ち上げを予定しています。
スクリーンショット 2020-01-18 19.52.33.png
(引用元: https://www.rymansat.com/satellites)

その中で、リーマンサット広報の方から

「宇宙にいる衛星(RSP-00,RSP-01)が、今どこにいるのか分かるといいよね?」

との聞かれたので、

「それ面白いですね!やりまーす?」(どうやって実装するか分からんけど、まぁなんとかなるっしょ)

と二つ返事で引き受けたのが、開発のきっかけです。

実装上のポイント

ここから実装について紹介します。このISSの軌道を表現する際に、重要なポイントが以下の2点です。

1. どうやって「バーチャル地球儀や衛星軌道を表示」するのか?
2. どうやって「ISSの軌道の計算」するのか?

では、この2点をどうやって実現したのかについて説明します。

1. バーチャル地球儀上での情報の可視化

Satellite Trackerの一番の根幹にあたるのが「バーチャル地球儀上での情報の可視化」です。これを実現するために利用したライブラリが「Cesium」です。

Cesium / バーチャル地球儀上での情報を可視化するOSS

Cesiumは、GoogleEarthのように3D地球儀上に様々なデータを表示できるOSSです。このアプリケーションの根幹に当たります。

スクリーンショット 2020-01-19 18.16.58.png

バーチャル地球儀の表示やISSの位置や衛星軌道の可視化、経過時間に応じたISS位置の移動はすべて「Cesium」によって実現されています。

他にもCesiumには様々な機能が提供されています。それらの機能については、公式サイトで多くのサンプルアプリケーション例が公開されています。
Cesium Sandcast

Resium / CesiumをReact.jsで実装できるOSS

先ほど紹介したCesiumはピュアなJavaScriptのみで実装するライブラリです。ただ、Resiumというライブラリを用いることでReact.jsを使った実装ができるようになります。

実際に実装の例を紹介すると、東京の位置に点をプロットする場合はViewerEntityの2つのコンポーネントを記述するだけです。非常に簡単ですね!

// src/App.tsx
import * as React from 'react';
import { Viewer, Entity } from 'resium';
import { Cartesian3 } from 'cesium';

const App: React.FC = () => {
  // Viewer: バーチャル地球儀や背景の宇宙を表示するCesiumのベースのコンポーネント
  // Entity: 点をプロットするコンポーネント
  return (
    <Viewer>
      <Entity
        description="test"
        name="tokyo"
        point={{ pixelSize: 10 }}
        // 東京の座標情報を指定(東経139度、北緯35度、高度100m)
        position={Cartesian3.fromDegrees(139.767052, 35.681167, 100)}
      />
    </Viewer>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));

上述のコードに対する表示は以下の通りです。東京の座標にデータがプロットされていることが分かります。
スクリーンショット 2020-01-19 18.47.57.png

CZML / Cesiumで多様な表現を生み出すデータ構造

ISSの衛星軌道を表現する上で、時間と座標を組み合わせた時系列データを扱う必要があります。その際に利用するがCZMLです。

CZMLはKMLデータ(三次元地理情報を扱う目的とした情報をXML形式で表現したデータ形式)をベースとしたCesium独自のデータ構造です。詳細は以下のドキュメントから確認することができます。

CZML Guide

CZMLで衛星軌道を表現する際に必要な情報は以下の3点です。

  1. position
  2. billboard
  3. path

Positionは以下のようにepochで指定した時間を基準に、0秒、60秒、120秒の位置を定義することで、ISSの軌道を表現できます。

position: {
    epoch: '2020-01-19T12:00:00Z',
    cartographicDegrees: [
        // 経過時間, 経度, 緯度, 高度
        0.0, 1.0, 2.0, 3.0,
        60.0, 4.0, 5.0, 6.0,
        120.0, 7.0, 8.0, 9.0
    ]
}

Billboardは、Positionで定義されたISS軌道に応じて移動させたいアイコンのスタイルを定義できます。また、Pathは、positionで定義されたISS軌道のスタイル情報を定義できます。
スクリーンショット_2020-01-20_2_27_21.png

以上のCZMLデータを元にISS軌道をResiumで実装する方法は、専用のコンポーネント(CzmlDataSource)にデータを指定するだけです。非常に簡単ですね!!

import * as React from 'react';
import { Viewer, CzmlDataSource } from 'resium';

const App: React.FC = () => {
  // czml: CZML形式のデータ
  const czml = [ ... ];
  return (
    <Viewer>
      <CzmlDataSource data={czml} />
    </Viewer>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));

2. 衛星軌道の計算

ここまでバーチャル地球儀上にどうやってISSの軌道を表示するかについて説明しました。次は、肝心の「ISSの衛星軌道の座標をどうやって計算するか?」 について説明します。

TLE / 衛星の動きを計算する際のベースの情報

衛星の軌道計算に最も重要な情報が「TLE」です。

TLE(二行軌道要素/Two Line Elements)は、「現在衛星がどこにいるのか、将来どのような動きをするのかを計算する際の基準となるデータ」です。TLEの詳細説明はここでは省かせて頂きますが、TLEには衛星の位置や軌道の形、衛星を識別番号といった情報を含んでいます。

TLEの一例として、以下にISSのTLE(2020/01/21時点)を記載します。

ISS (ZARYA)             
1 25544U 98067A   20020.35627297  .00000528  00000-0  17475-4 0  9998
2 25544  51.6461   4.8684 0004966 149.6593 354.0517 15.49574886208942

実際に使用するTLEは、以下のNORAD(北アメリカ航空宇宙防衛司令部)のサイトより取得を行いました。
CelesTrak: Current NORAD Two-Line Element Sets

pyorbital / 衛星軌道を計算するライブラリ

次に、TLEを元にISSの衛星軌道を計算する方法について説明します。

今回はpyorbitalというPythonのライブラリを利用しました。pyorbitalはTLEと時間を指定するだけで、衛星軌道上における衛星の現在位置(経度、緯度、高度)を計算できます。

pyorbitalを使った実際のプログラムを以下に記載します。今回は60秒毎のISSの位置をそれぞれ計算しています。

import math
from datetime import datetime, timedelta
from pyorbital.orbital import Orbital
from pyorbital.tlefile import Tle

# TLE情報を使いpyorbitalのOrbitalオブジェクトを初期化
iss_tle = Tle('ISS (ZARYA)', 'app/tle/iss.tle')
iss_orbit = Orbital('ISS (ZARYA)', line1=iss_tle.line1, line2=iss_tle.line2)

# 現在時刻の取得(UTC)
start = datetime.utcnow()

# 現在時刻を基準として60秒毎の衛星位置の計算をループ
result = []
for i in range(100):
    # get_lonlatalt()メソッドで、TLE情報と時間から衛星の位置を計算
    # lon:経度、lat:緯度、alt:高度(km)
    utc_time = start + timedelta(minutes=i)
    lon, lat, alt = iss_orbit.get_lonlatalt(utc_time)
    result.append(i * 60)
    result.append(lon)
    result.append(lat)
    result.append(alt * 1000) # CZML用に高度の単位をキロメートル(km)からメートル(m)へ変換

# 結果の表示
print(result)
'''
=> [
    0, -83.94122065760396, -33.30684677745626, 426849.0101226178, 
    60, -80.66011932440666, -35.862064816018126, 428099.53216608084, 
    120, -77.15140528356159, -38.305304602142364, 429323.9356303332, 
    180, -73.39030404329198, -40.61863379606101, 430506.5119570222, 
    240, -69.35366236896286, -42.78184592250954, 431632.1126424749, 
    .....
]
'''

本番環境のアーキテクチャ

ここまでISSの軌道を表示するために必要なライブラリやその使い方について説明しました。最後は、アプリを公開するための本番環境のアーキテクチャについて説明します。

以下の図が本番環境のアーキテクチャの概略図です。

Architecture.png

フロントエンドはReact/TypeScriptによるSPAです。そして、Cesium/Resiumを使ってバーチャル地球儀やISSの衛星軌道の表現を行います。

次に、フロントエンドのAssetファイル群は、Firebase上にHositingをしています。Firebaseは単に静的ファイルをホスティングするだけであれば、個人開発レベルでは無料枠で利用可能です。

一方で衛星軌道の計算はCloud FunctionsによるAPIを提供する形で実装しました。衛星軌道計算のAPIはPythonによって実装されています。Cloud Functionsについても無料枠があります。

最後に、衛星起動に用いるTLEについて説明します。TLEについては、固定の値ではなく時間が経過するにつれて最新情報に更新される必要があります。更新が行われないと計算の結果である衛星軌道に誤差が生じてしまいます。今回使用したTLEは2019/01/21現在の情報です(定期的な更新処理作成が間に合わなかったので)。予めご了承ください。

今回の作成したアプリケーションのコード

実装についてもっと詳しく知りたい方は、以下のリポジトリでコードを共有しております。

SatelliteTracker

最後に

ISSの軌道を可視化するアプリについて紹介しました。

この記事や共有したソースコードを読んで

「CesiumやResiumやpyorbitalていうライブラリ凄え!!自分も使ってみたい!!」
「宇宙と人工衛星について、興味が湧いた!!」

と思う人が増えれば嬉しいです☺️

次回 12日目は@ukisoftさんの「社外の開発コミュニティに参加してみての感想」です。乞うご期待!!!

参考文献・リンク

Cesium

衛星軌道

Firebase

Google Cloud Functions

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む