4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FOSS4GAdvent Calendar 2024

Day 6

地球儀っぽく🌏

Last updated at Posted at 2024-12-06

FOSS4G Advent Calendar 2024 の6日目です。

最初は GDAL で何か書けないかなと思ってましたが、ネタが微妙だったので5月17日に行われた FOSS4G もくもく会 #002 でもくもくしたときのことをまとめます。

目的

地軸が傾いた地球を描きたい。

できあがり

globe_rotation.gif

d3.js をつかって

多くの GIS ソフトウェアでは、基本的に北を上にした「地図」を前提としています(作図した地図を回転させることができるものもあります)。

しかし地球の自転軸は公転面(黄道面)に対し約23.4度傾いています。これを何とかして描画したい! というわけで d3.js で描いてみました。

d3.js はデータビジュアライズのためのライブラリで、位置データ以外にも様々なデータをグラフィカルに表現でき、地図投影法もサポートしています。

純粋な GIS のためのライブラリでないため(?)か、非常に柔軟な表現ができ、投影法も既存のものから任意の数式までサポートしています。たとえば、地球から地図に投影する様子のアニメーションだって可能です。

そして、 d3.js には projection.rotate(lambda, phi, gamma) があります。これは地図ではなく地球の3軸に対し回転角を設定することができるものです。

地球における X 軸は本初子午線と赤道の交点方向、 Y 軸は東経90度子午線と赤道の交点方向、 Z 軸は北極方向です。各引数の lambda は Z 軸の回転角( +X から +Y 方向)、 phi は Y 軸の回転角( +X から +Z 方向1)、 gamma は X 軸の回転角( +Y から +Z 方向)を表し、いわゆるヨー角、ピッチ角、ロール角にあたります。

ソースコード

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <script src="https://unpkg.com/topojson@3"></script>
    <script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
    <script src="https://cdn.jsdelivr.net/npm/d3-geo@3"></script>
    <script src="https://cdn.jsdelivr.net/npm/d3-geo-projection@4"></script>
  </head>
  <body>
    <div id="container" style="margin:50px;"></div>
    <script type="module">
      // データの読み込み
      // TopoJSON に変換した NaturalEarth
      const world = await d3.json("./land-50m.json");
      const land = topojson.feature(world, world.objects.land);
      const graticules = await d3.json("./ne_10m_graticules_15.geojson");

      // 投影法および大きさなどの設定
      let sphere = ({type: "Sphere"});
      let projection = d3.geoOrthographic();

      const width = 600;
      const [[x0, y0], [x1, y1]] = d3.geoPath(projection.fitWidth(width, sphere)).bounds(sphere);
      const height = Math.ceil(y1 - y0), l = Math.min(Math.ceil(x1 - x0), height);
      projection.scale(projection.scale() * (l - 1) / l).precision(0.2);

      // canvas の用意
      let canvas = d3.select("div#container").append('canvas')
        .attr("width", width)
        .attr("height", height);
      let context = canvas.node().getContext('2d');
      let path = d3.geoPath(projection, context);

      // 地球の描画を行う関数
      function renderGlobe(lambda, phi, gamma){
        // 回転角によって回転させる
        projection.rotate([lambda, phi, gamma]);
        context.clearRect(0, 0, width, height);
        context.beginPath(), path(sphere), context.fillStyle = "#fff", context.fill();
        context.beginPath(), path(land), context.fillStyle = "#000", context.fill();
        context.beginPath(), path(graticules), context.strokeStyle = "#666", context.stroke();
        context.beginPath(), path(sphere), context.stroke();
      }

      // 初期の回転角
      const lambda0 = 110.0;
      const phi0    = -16.67;
      const gamma0  = -16.67;
      renderGlobe(lambda0, phi0, gamma0);

      // アニメーション
      let i = 0;
      const tick_angle = 2;
      const t = d3.timer(elapsed => {
        renderGlobe(lambda0+i/tick_angle, phi0, gamma0);
        i = (i+1) % (360*tick_angle);
      }, 10);
    </script>
  </body>
</html>

苦労したところ

d3.js は豊富にギャラリーが用意されているのですが、 observablehq.com というサービス上で公開されています。

今回のソースもいろいろ参考にしています。もくもく会だったこともあり、ほぼ切り貼りですが。 d3.js はもっと勉強したいところ。

Observable は Jupyter lab みたいにセルにコードを入れて実行することができるみたいなんですが、実行順が上から読むのか下から読むのかよくわからなかったり、 FileAttachment などの Observable の機能が使われていたりと、純粋な HTML + JavaScript で実現する方法を確認するのに手間取りました。

あと最近の JavaScript の常識をあんまり知らず、 await は同期非同期の指定くらいの認識で、軽い処理ならあってもなくても大差ないのかな。と最初省いて書いてしまっていました。

      const world = d3.json("./land-50m.json"); // await を書かなかった
      const land = topojson.feature(world, world.objects.land);

上記のように記述したとき、2行目で land が取り出せない。なんでだ、としばらく悩み続け、ようやく JSON オブジェクトではなく Promise が返っていることに気付きました。理解できてないなら、ちゃんと写経しないとダメですね。

おまけ

自由に回転させることができるので、凝った斜軸投影も可能となります。

image.png

これは全大陸が切れ目なく収まる斜軸モルワイデくん2

  1. 普通は Y 軸の回転は +Z から +X 方向が正だと思うのですが

  2. https://www2.jpgu.org/meeting/2011/yokou/MTT033-P01.pdf

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?