LoginSignup
1
1

フライトマップ風の地図(マップアニメーション)をつくる

Last updated at Posted at 2024-02-18

 前回の記事で、"指定した2点間を地球が球体なことを考慮し最短経路で結ぶ”地図(フライトマップ風の地図)を作成するスクリプトを掲載したが、この地図をマップアニメーション化することを考える。

1. マップアニメーションの作成

image.png

 マップアニメーションは、LeafletJavaScriptsetIntervalanimate関数を併用し作成する。(HTML上にJavaScript(Leafletとアニメーションの関数)を記述。)
 Leaflet自体にはアニメーション機能がないため、Foliumでの操作はできない。

image.png
  HTMLJavaScriptの関係は上記の通り(「HTMLとJavaScriptってなんだ...?」という思いからまとめたもの)。
 今回の地図作成では、Google Colaboratoryでコード作成/実行/結果確認をしたいので、マジックコマンドHTMLテンプレートの利用により、python上で全ての作業が完結するようにした。

2-a. フライトマップ風の地図(マップアニメーション)

 スクリプトは以下の通り。

MapAnime_flight.py
# 羽田からDallasまでの飛行ルートマップ風のアニメーションを描くスクリプト
# マジックコマンドを利用してPython上で生成から出力まで実行

%%html
<!DOCTYPE html>
<html>
<head>
<title>Great Circle Animation</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
</head>
<body>
<div id="map" style="height: 500px;"></div>

<script>
  var map = L.map('map').setView([35.5533, 135], 3); // 中心を東経135度に変更

  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    maxZoom: 18,
  }).addTo(map);

  var startPoint = L.latLng(35.5533, 139.7811); // Haneda Airport
  var endPoint = L.latLng(32.7767, -96.7970); // Dallas

  var polyline = L.polyline([], { color: 'blue' }).addTo(map);
  var points = [];
  var numPoints = 100;
  var index = 0;

  function animateLine() {
    var lat1 = startPoint.lat * Math.PI / 180; // 緯度1をラジアンに変換
    var lon1 = startPoint.lng * Math.PI / 180; // 緯度2をラジアンに変換
    var lat2 = endPoint.lat * Math.PI / 180; // 経度1をラジアンに変換
    var lon2 = endPoint.lng * Math.PI / 180; // 経度2をラジアンに変換

    var d = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin((lat1 - lat2) / 2), 2) +
      Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin((lon1 - lon2) / 2), 2))); //球面三角法のハーフバーシンの公式を使用して2点間の角度を求めその角度から大円距離を計算

    for (var i = 0; i <= numPoints; i++) {
      var f = i / numPoints;
      var A = Math.sin((1 - f) * d) / Math.sin(d);
      var B = Math.sin(f * d) / Math.sin(d);
      var x = A * Math.cos(lat1) * Math.cos(lon1) + B * Math.cos(lat2) * Math.cos(lon2);
      var y = A * Math.cos(lat1) * Math.sin(lon1) + B * Math.cos(lat2) * Math.sin(lon2);
      var z = A * Math.sin(lat1) + B * Math.sin(lat2);
      var lat = Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))) * 180 / Math.PI;
      var lon = Math.atan2(y, x) * 180 / Math.PI;

      // 経路を-45度から315度の範囲に修正
      if (lon < -45) {
        lon += 360;
      } else if (lon > 315) {
        lon -= 360;
      }

      points.push(L.latLng(lat, lon));
    }

    animate();
  }

  function animate() {
    if (index < points.length) {
      polyline.addLatLng(points[index]);
      index++;
      setTimeout(animate, 100); // アニメーション速度を調整するための遅延
    }
  }

  animateLine(); // アニメーションを開始
</script>
</body>
</html>

 gistはこちら → gist:MapAnime_flight.py

 このスクリプトを掻い摘んで説明する。

① マジックコマンドの利用を宣言

%%html

 生成から実行、出力結果確認までPython上で実行するため、「%%html」でHTMLの記述を囲む。

② 羽田空港とDallasをそれぞれ出発点と到着点に設定

 var startPoint = L.latLng(35.5533, 139.7811); // Haneda Airport
 var endPoint = L.latLng(32.7767, -96.7970); // Dallas

③ スクリプト内で2点間の経路を求め、経路上の各点の座標を配列に格納

  function animateLine() {
    var lat1 = startPoint.lat * Math.PI / 180; // 緯度1をラジアンに変換
    var lon1 = startPoint.lng * Math.PI / 180; // 緯度2をラジアンに変換
    var lat2 = endPoint.lat * Math.PI / 180; // 経度1をラジアンに変換
    var lon2 = endPoint.lng * Math.PI / 180; // 経度2をラジアンに変換

    var d = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin((lat1 - lat2) / 2), 2) +
      Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin((lon1 - lon2) / 2), 2))); //球面三角法のハーフバーシンの公式を使用して2点間の角度を求めその角度から大円距離を計算

    for (var i = 0; i <= numPoints; i++) {
      var f = i / numPoints;
      var A = Math.sin((1 - f) * d) / Math.sin(d);
      var B = Math.sin(f * d) / Math.sin(d);
      var x = A * Math.cos(lat1) * Math.cos(lon1) + B * Math.cos(lat2) * Math.cos(lon2);
      var y = A * Math.cos(lat1) * Math.sin(lon1) + B * Math.cos(lat2) * Math.sin(lon2);
      var z = A * Math.sin(lat1) + B * Math.sin(lat2);
      var lat = Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))) * 180 / Math.PI;
      var lon = Math.atan2(y, x) * 180 / Math.PI;

 
2点間の距離(弧の長さ)計算は今回のスクリプトでは’ハバーサインの公式’が使われている。
(参考→)Calculate distance, bearing and more between Latitude/Longitude points

④ animate 関数でアニメーションを描写

  function animate() {
    if (index < points.length) {
      polyline.addLatLng(points[index]);
      index++;
      setTimeout(animate, 100); // アニメーション速度を調整するための遅延

 animate 関数は、points 配列に格納された座標を順番に取り出し、それを polyline に追加する。

 実行結果をgif化したものは以下の通り。
fly_route2.gif

 以上で、フライトマップ風の地図(マップアニメーション)を作成することができた。

3. 参考文献

「GISファイルフォーマットの簡単なまとめ/地図のワークブック」
Leaflet
Leafletの使い方(埼玉大学教育学部人文地理学 谷謙二研究室)
OpenStreetMap Japan
TRAINOCAMP「HTMLとJavaScriptを使いこなす!」
BrainGate「マークアップ(Markup)とは?」(マークアップ言語とプログラミング言語の違い)
Jupyter Notebook マジックコマンド自分的まとめ
Jupyter Notebookでマジックコマンド一覧を確認する%lsmagic
Calculate distance, bearing and more between Latitude/Longitude points
Jetlovers
chatGPT 3.5

1
1
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
1
1