d3.js で天球儀

# FOSS4G で天球儀

ある日のこと。ふと北天周辺の星座の位置関係を確認したくなったときのこと。そのときは手元に天体関係の書籍もなく、しょうがないので [Google Sky](https://www.google.co.jp/intl/ja/sky/#latitude=71.25141338960526&longitude=54.4921875&zoom=2&slide=3&mI=1&oI=1) にアクセス。

...
......
.........

もしかして、お前も Pseudo Mercator か！　地球の場合、極圏を表示できなくても日常的には問題ありませんが、天体の場合はそれだとだめです。だめだめです。

そのときは星座の確認は諦めましたが、同時に天体データを GeoJSON とかで整理し、適切に投影座標系を設定した GIS を使えば天球儀が作れるのでは？　と思ってチャレンジしてみました。

ネタバレ：時間内にできませんでした。

## 使用ツールの選定

表示のためのライブラリとして Leaflet.js とか OpenLayers とか考えてみましたが、自由な投影座標系の設定は難しそうでした。ということで d3.js 。

テキスト処理で Ruby のスクリプトを書く予定なので、別に使わなくてもいいのですが、今回は FOSS4G Advent Calendar ということでデータの一時保管のところで PostGIS も使うことにします。

## 使用するデータ

### 天体データ

利用する天体のデータとして、全天を網羅してて、コンピュータで扱いやすく、こういった用途において比較的使用されているヒッパルコス星表を利用することとします。ただ、ヒッパルコス星表の座標は J1991.25[^1] を元期とした ICRS[^2] 座標なので J2018.0 の赤道座標系[^3]への変換作業は行います。普通は J2000.0 を使いますが、今回はせっかくなので。また、固有運動[^4]の計算もしておきます。

* [I/239 The Hipparcos and Tycho Catalogues (ESA 1997) - VizieR archives](http://cdsarc.u-strasbg.fr/viz-bin/Cat?I/239)

### 星座データ

現代の星座は国際天文学連合が定め、名称・略称・境界を規定しています。境界データはダウンロードできるようになっているので、これを利用します。座標は J2000.0 の赤道座標系で記載されているので J2018.0 の赤道座標系に変換します。

* [The Constellations | IAU](https://www.iau.org/public/themes/constellations/)

### 星座線データ

星座線は正式に決められたものはなく、文化や思想により異なります。すなわち著作物といえ、出版物などの星座線をそのまま使うことはできません。そこで自由に使える星座線がないか探したところ、 Astro Commons さんが星座線データをパブリックドメインで公開されていたので使わせてもらうことにします。

データは始終点をヒッパルコス星表の ID で参照する形式になっているので、座標変換等は必要ありません。

* [ヒッパルコス星表 星座線データ - Astro Commons](http://astronomy.webcrow.jp/hip/#line)

## データの整形

PostGIS へデータを保管したいわけですが、 PostgreSQL への一括データについて調べてみると [COPY](https://www.postgresql.jp/document/9.4/html/sql-copy.html) を使うとよいようです。 Ruby を使って PostgreSQL に投げれる形に整形します。

ついでにこのタイミングで天体の座標変換も行ってしまいます。

そのほか、星座のラベルをつけるために星座ポリゴンの重心を求める処理など PostGIS でできる処理は SQL 書いて実行させます。最終的に PostGIS から GeoJSON を出力し、 topojson で TopoJSON に変換しました。

## d3.js で可視化

検索すると地図描画のサンプルがたくさんみつかりました。ただ、最新版の v4 ではなく v3 を使ったサンプルだったりで、試行錯誤しつつも表示させることができました。

<img width="517" alt="可視化その1" src="https://qiita-image-store.s3.amazonaws.com/0/215290/5f67ade9-4d53-b697-f3b5-e9f7d5f9203f.png">

冬の星座の代表格。オリオン座です。

けれど、よくよく見てみると少しおかしなことになっています。

正解はこちら。

<img width="50%" alt="オリオン座" src="https://qiita-image-store.s3.amazonaws.com/0/215290/02190e6e-e223-1d57-3582-35db7505bd3d.gif">

[(C) IAU, Sky & Telescope magazine](https://www.iau.org/public/themes/constellations/#ori) / CC BY 3.0

見比べてもらうとわかりますが、左右反転しています。下の図に書かれている経度は右から 4h (= 60°), 5h (= 75°), 6h (= 90°), ... となっています。天球の場合、これでよいのです。

これは内側（地上）から外側に向かって観測を行っているのでこのようなことになります。地底人さんも地球表面の座標を扱うときは同じ苦労があることだと思います。

幸いにも d3.js は任意の投影法を定義することができるので、 x を反転させた投影法を定義することで解決できました。今回は [stereographic.js](https://github.com/d3/d3-geo/blob/master/src/projection/stereographic.js) にしました。

```javascript
var projection = d3.geoProjection(function(x, y) {
        var cy = Math.cos(y);
        var k = 1 + Math.cos(-x) * cy;
        return [cy * Math.sin(-x) / k, Math.sin(y) / k];
    })
    .scale(600)
    .translate([width / 2, height / 2])
    .rotate([280, -15])
    .clipAngle(100);
```

## とりあえずの成果品

<img width="1158" alt="可視化その2" src="https://qiita-image-store.s3.amazonaws.com/0/215290/f2ae53e5-90f6-d01d-695f-6dddebf37939.png">

右側のうお座（Psc）のあたりに変な線が集まっていたり、変な場所におとめ座（Vir）のラベルがあったりと、座標変換のところでバグがいるのか、入力データに不正値が入っているのかするようですが、精査の時間がなくて断念。

あとはマウスのドラッグでぐりぐり動かせるようにしたくて、ちょっとがんばってみたものの、うまくマウスの動きと連動できませんでした。

### 今後の展望（希望）

* ある程度ちゃんとできたら、ソースを公開
* 背景を黒にし、色指数をもとにした星の着色
* 日月惑星の位置を計算して表示
* QGIS でもちゃんと表示されるような投影法の定義ができたらいいな
  * そしたら SpatiaLite あたりに変換してオープンデータにしてもいいかも

## 今日のまとめ

* ジオ成分なかった
* d3.js は投影の自由度高いけど、なかなか難しい
* ふたご座流星群の見頃は12/13〜12/15くらい。今年は月明かりの影響も少ないし、あったかくして挑もう！


----

[^1]: J2000.0 は地球時の 2000-01-01 12:00:00 と定義されているため、 J1991.25 は 1991-04-02 13:30:00 、 J2018.0 は 2018-01-01 00:00:00 にあたる。
[^2]: 測地で使われる ITRF の天球座標版。年代に関わらず、固定された座標軸として扱うことができる系。
[^3]: 地球の赤道面を XY 平面、地球の自転軸を Z 軸とする座標系。地球の自転軸は約25,800年周期で自転軸の向きが一回転するため、赤道座標系の座標軸も時代とともに移動する。
[^4]: 太陽系も各恒星系も系自体移動しているため、天体の位置がわずかながら変化する運動のこと。