Googleマップに太陽系を描く

(1/1)
無限に広がる大宇宙――太陽系ですら、やたらに広い。『宇宙戦艦ヤマト』のように、小惑星を集めて船体防御に使うなどということは、現実には不可能である。
学生時代、この太陽系の広さを実感してもらうために、東京駅に太陽が、横浜駅に地球があったとしたら、太陽系はどこまで広がっているか、日本地図の上にマッピングし、各地を撮影した8ミリ映画を作ったことがある。
今回は、同じことをGoogleマップとJavaScriptで実装してみることにする。

サンプル・プログラムの実行例

Googleマップに太陽系を描く

サンプル・プログラムのダウンロード

圧縮ファイルの内容
gmapSolarSystem.htmlサンプル・プログラム本体。

準備

  20: <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  21: <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB-LE2H8D9At3-J7aqwuQW2oJ-_ulXcozA&region=JP&libraries=geometry"></script>

jQueryGoogleマップAPI を利用する。

まず、GoogleマップAPIを利用するにはキーが必要となる。
Google Maps APIs for Web」にアクセスし、「キーを取得」ボタンをクリックすると無料で取得できる。これを "https://maps.googleapis.com/maps/api/js?key=" に続く部分に記入すること。ダウンロードしたコンテンツに書かれているキーは、当サイトでしか利用できない。また、Google Cloud Console に入り、GoogleMaps JavaScript API、Google Maps Geocoding API を有効にする。
なお、後述するとおり geometryライブラリを利用するので、URLのオプションに libraries=geometry を加えてある。

  23: //Googleマップ用パラメータ
  24: var gmap;
  25: var gmapid = 'gmap';                //マップID
  26: var gwidth  = 600;                  //地図幅
  27: var gheight = 400;                  //地図高
  28: var msize   = 32;                   //惑星マーカーのサイズ
  29: 
  30: //初期値
  31: var lat_center = 35.681111;     //地図の中心点(緯度)=東京駅
  32: var lng_center = 139.766667;        //      (経度)
  33: var lat_sun    = 35.681111;     //太陽の位置(緯度)=東京駅
  34: var lng_sun    = 139.766667;        //     (経度)
  35: var lat_earth  = 35.466111;     //地球の位置(緯度)=横浜駅
  36: var lng_earth  = 139.6225;          //     (経度)
  37: var zoom       = 7;             //ズームの初期値
  38: var maptype    = 'roadmap';     //マップタイプ

Googleマップ用のパラメータと初期値。これらは自由に変更できる。
なお、惑星マーカーのサイズ msize は、後述する太陽のアイコン、惑星の基本情報に指定するものと同じであること。

  44: //太陽のアイコン
  45: const sun = {
  46:     icon: 'http://iconhoihoi.oops.jp/sozai/icon/166-planet/icon_1r_32.png'
  47: }
  48: //惑星の基本情報
  49: // radius:軌道半長径(AU),icon:アイコンファイル,
  50: // color:軌道描画色,weight:軌道の太さ,
  51: // heading:アイコン配置位置(真北から時計回りの角度)
  52: let planets = {
  53:     mercury: {
  54:         radius: 0.3871,
  55:         icon: 'http://iconhoihoi.oops.jp/sozai/icon/166-planet/icon_2y_32.png',
  56:         color: '#FF0000',
  57:         weight: 1,
  58:         heading: 45
  59:     },
  60:     venus: {
  61:         radius: 0.7233,
  62:         icon: 'http://iconhoihoi.oops.jp/sozai/icon/166-planet/icon_3g_32.png',
  63:         color: '#FF0000',
  64:         weight: 1,
  65:         heading: 90
  66:     },

太陽のアイコン・ファイルは sun.icon に、惑星の情報は planets に格納する。
惑星の情報は、コメントにあるように、radius:軌道半長径(AU),icon:アイコンファイル,color:軌道描画色,weight:軌道の太さ,heading:アイコン配置位置(真北から時計回りの角度)から成る。
円軌道でよければ、変数 planets に情報を追加することで、いくつでも描くことができる。

解説:Googleマップとイベントの準備

 280: /**
 281:  * Googleマップ関係
 282: */
 283: google.maps.event.addDomListener(window, 'load', function() {
 284:     var mapdiv = document.getElementById(gmapid);
 285:     var myOptions = {
 286:         zoom: zoom,
 287:         center: new google.maps.LatLng(lat_center, lng_center),
 288:         mapTypeId: google.maps.MapTypeId.maptype,
 289:         mapTypeControl: true,
 290:         scaleControl: true
 291:     };
 292:     gmap = new google.maps.Map(mapdiv, myOptions);
 293: 
 294:     //初期化
 295:     initialize();
 296: 
 297:     //event.addListener
 298:     google.maps.event.addListener(gmap, 'click', function(e) {
 299:         onClickMap(e.latLng);
 300:     });
 301: });

Googleマップの準備は定番である。
後述する初期化のための関数 initialize を呼び出し、クリック・イベント発生時に呼び出す関数として onClickMap を登録する。

解説:初期化処理

 231: /**
 232:  * 初期化処理
 233:  * @param   なし
 234:  * @return  なし
 235: */
 236: function initialize() {
 237:     //太陽の位置、マーカー
 238:     l_sun = new google.maps.LatLng(lat_sun, lng_sun);
 239:     //描画済みのマーカーがあれば消去
 240:     if (markers['sun'!null) {
 241:         markers['sun'].setMap(null);
 242:     }
 243:     //マーカーを描く
 244:     markers['sun'] = new google.maps.Marker({
 245:         position: l_sun,
 246:         icon: new google.maps.MarkerImage(
 247:             sun.icon,
 248:             new google.maps.Size(msize, msize),
 249:             new google.maps.Point(0, 0),
 250:             new google.maps.Point(msize / 2, msize / 2),    //位置調整
 251:         ),
 252:         map: gmap
 253:     });
 254: 
 255:     //地球の位置、マーカー
 256:     l_earth = new google.maps.LatLng(lat_earth, lng_earth);
 257:     //描画済みのマーカーがあれば消去
 258:     if (markers['earth'!null) {
 259:         markers['earth'].setMap(null);
 260:     }
 261:     //マーカーを描く
 262:     markers['earth'] = new google.maps.Marker({
 263:         position: l_earth,
 264:         icon: new google.maps.MarkerImage(
 265:             planets['earth'].icon,
 266:             new google.maps.Size(msize, msize),
 267:             new google.maps.Point(0, 0),
 268:             new google.maps.Point(msize / 2, msize / 2),    //位置調整
 269:         ),
 270:         map: gmap
 271:     });
 272: 
 273:     //距離計算
 274:     var distance = google.maps.geometry.spherical.computeDistanceBetween(markers['sun'].position, markers['earth'].position);
 275: 
 276:     //太陽系を描く
 277:     drawSolarSystem(markers['sun'].position, distance);
 278: }

ユーザー関数 initialize は、冒頭の初期値に基づき、マップ上に太陽系を描くものである。
まず、太陽の位置(緯度・経度)を取り出し、マーカーとして表示する。このとき、あとでマーカーを移動するために、マーカーはオブジェクト変数 markers に代入しておく。
地球についても同様に処理する。

次に、惑星軌道を描く基準となる、太陽=地球間の距離を求める。
GoogleマップAPIの geometryライブラリには2点間の距離計算メソッド spherical.computeDistanceBetween が用意されているので、これを利用することにする。

これらが揃ったら、太陽系を描画するユーザー関数 drawSolarSystem を呼び出す。

解説:各惑星の軌道を描き、マーカーを設置する

 121: /**
 122:  * 惑星の軌道を描く
 123:  * @param   object latlng   太陽の位置(緯度・経度)
 124:  * @param   double distance 太陽=地球距離(メートル)
 125:  * @return  なし
 126: */
 127: function drawSolarSystem(latlng, distance) {
 128:     //惑星の数だけ繰り返す
 129:     for (let key in planets) {
 130:         radius = distance * planets[key].radius;    //軌道半長径
 131:         //描画済みの円があれば消去
 132:         if (orbits[key!null) {
 133:             orbits[key].setMap(null);
 134:         }
 135:         //円を描く
 136:         orbits[key] = new google.maps.Circle({
 137:             map: gmap,
 138:             clickable: false,
 139:             center: latlng,                 //中心点
 140:             radius: radius,                     //半径(メートル)
 141:             fillOpacity: 0.0,                   //塗りつぶし透過度
 142:             strokeColor: planets[key].color,    //外周色 
 143:             strokeWeight: planets[key].weight   //外周太さ(ピクセル)
 144:         });
 145: 
 146:         //惑星マーカーを設置
 147:         if (key !'earth') {
 148:             p_latlng = google.maps.geometry.spherical.computeOffset(latlng, radius, planets[key].heading);
 149:             //描画済みのマーカーがあれば消去
 150:             if (markers[key!null) {
 151:                 markers[key].setMap(null);
 152:             }
 153:             //マーカーを描く
 154:             markers[key] = new google.maps.Marker({
 155:                 position: p_latlng,
 156:                 icon: new google.maps.MarkerImage(
 157:                     planets[key].icon,
 158:                     new google.maps.Size(msize, msize),
 159:                     new google.maps.Point(0, 0),
 160:                     new google.maps.Point(msize / 2, msize / 2),    //位置調整
 161:                 ),
 162:                 map: gmap
 163:             });
 164:         }
 165:     }
 166: }

ユーザー関数 drawSolarSystem は、太陽の位置(緯度・経度)と太陽=地球間の距離を引数に、マップ上に各惑星の軌道を描き、惑星マーカーを設置するものである。

GoogleマップAPIには円を描画するメソッド Circle が用意されている。
惑星の軌道は、正確には楕円であるが、今回はメソッド Circle を利用することでプログラムを簡素化した。軌道がかなり楕円になっている冥王星は、今回は除外した。

あとで軌道を移動するために、メソッド Circle で描いたオブジェクト情報はオブジェクト変数 orbits に格納しておく。

ユーザー関数 initialize と同様に、各惑星のマーカーを軌道上に設置してゆく。
設置場所(緯度・経度)は、太陽の位置から planets[].heading で指定する方向に、軌道半長径だけ離れた位置に設置することにする。GoogleマップAPIの geometryライブラリに用意されているメソッド spherical.computeOffset を利用することで、その位置を求めている。
なお、太陽や地球と同様、あとで軌道を移動するために、マーカーはオブジェクト変数 markers に代入しておく。

解説:各惑星の軌道、マーカーを消去する

 168: /**
 169:  * 各惑星の軌道、マーカーを消去する
 170:  * @param   なし
 171:  * @return  なし
 172: */
 173: function clearSolarSystem() {
 174:     for (let key in planets) {
 175:         //描画済みのマーカーがあり、地球以外なら消去
 176:         if ((markers[key!null&& (key !'earth')) {
 177:             markers[key].setMap(null);
 178:         }
 179:         //描画済みの円があれば消去
 180:         if (orbits[key!null) {
 181:             orbits[key].setMap(null);
 182:         }
 183:     }
 184: };

ユーザー関数 clearSolarSystem は、描画済みの惑星の軌道(円)と、惑星マーカーを消去する。
描画時に代入したオブジェクト変数 orbits および markers を参照しながら、GoogleマップAPIメソッド setMap で消去していく。

解説:地図クリック時の処理

 186: /**
 187:  * 地図クリック時の処理
 188:  * @param   object lat_lng クリックされた座標
 189:  * @return  なし
 190: */
 191: function onClickMap(lat_lng) {
 192:     //太陽と地球のどちらが選ばれているか
 193:     var radios = document.myform.selector;
 194:     for (var i = 0i < radios.lengthi++) {
 195:         if (radios[i].checked) {
 196:             selector = radios[i].value;
 197:             break;
 198:         }
 199:     }
 200:     var longitude = 'lng_' + selector;
 201:     var latitude  = 'lat_' + selector;
 202:     if (selector == 'sun') {
 203:         icon = sun.icon;
 204:     } else {
 205:         icon = planets['earth'].icon;
 206:     }
 207: 
 208:     //描画済みのマーカーがあれば消去
 209:     if (markers[selector!null) {
 210:         markers[selector].setMap(null);
 211:     }
 212:     //マーカーを描く
 213:     markers[selector] = new google.maps.Marker({
 214:         position: lat_lng,
 215:         icon: new google.maps.MarkerImage(
 216:             icon,
 217:             new google.maps.Size(msize, msize),
 218:             new google.maps.Point(0, 0),
 219:             new google.maps.Point(msize / 2, msize / 2),    //位置調整
 220:         ),
 221:         map: gmap
 222:     });
 223: 
 224:     //距離計算
 225:     var distance = google.maps.geometry.spherical.computeDistanceBetween(markers['sun'].position, markers['earth'].position);
 226: 
 227:     //太陽系を描く
 228:     drawSolarSystem(markers['sun'].position, distance);
 229: }

ユーザー関数 onClickMap は、地図をクリックした際に呼び出される関数で、処理的には initialize に似ている。
違う点は、冒頭で、太陽と地球のどちらのラジオボタンが選ばれているかを調べ、それによって、クリックされた座標を太陽に割り当てるか、地球に割り当てるかを場合分けしているところである。

参考サイト

参考書籍

表紙 宇宙はどこまで行けるか
著者 小泉宏之
出版社 中央公論新社
サイズ 新書
発売日 2018年09月20日頃
価格 1,100円(税込)
ISBN 9784121025074
月や小惑星で資源を採掘する。火星に有人探査機を送る。木星や土星の衛星で生命の痕跡を探すー。ベンチャー企業が勃興し、宇宙が新たなビジネスの主戦場になりつつある今、こうした計画は現実のものになろうとしている。さらに、4光年離れた隣の惑星系への探査計画さえ立ち上がっている。「はやぶさ」プロジェクトに携わり、世界初の小型イオンエンジン実用化を果たした若き研究者が、人類の可能性の限界に迫る。
 
(この項おわり)
header