Googleマップに太陽系を描く

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

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

Googleマップに太陽系を描く

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

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

準備

0020: <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
0021: <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 を加えてある。

0023: //Googleマップ用パラメータ
0024: var gmap;
0025: var gmapid = 'gmap';                //マップID
0026: var gwidth  = 600;                   //地図幅
0027: var gheight = 400;                   //地図高
0028: var msize   = 32;                    //惑星マーカーのサイズ
0029: 
0030: //初期値
0031: var lat_center = 35.681111;       //地図の中心点(緯度)=東京駅
0032: var lng_center = 139.766667;      //      (経度)
0033: var lat_sun    = 35.681111;       //太陽の位置(緯度)=東京駅
0034: var lng_sun    = 139.766667;      //     (経度)
0035: var lat_earth  = 35.466111;       //地球の位置(緯度)=横浜駅
0036: var lng_earth  = 139.6225;            //     (経度)
0037: var zoom       = 7;              //ズームの初期値
0038: var maptype    = 'roadmap';        //マップタイプ

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

0044: //太陽のアイコン
0045: const sun = {
0046:     icon: 'https://iconhoihoi.oops.jp/sozai/icon/166-planet/icon_1r_32.png'
0047: }
0048: //惑星の基本情報
0049: // radius:軌道半長径(AU),icon:アイコンファイル,
0050: // color:軌道描画色,weight:軌道の太さ,
0051: // heading:アイコン配置位置(真北から時計回りの角度)
0052: let planets = {
0053:     mercury: {
0054:         radius: 0.3871,
0055:         icon: 'https://iconhoihoi.oops.jp/sozai/icon/166-planet/icon_2y_32.png',
0056:         color: '#FF0000',
0057:         weight: 1,
0058:         heading: 45
0059:     },
0060:     venus: {
0061:         radius: 0.7233,
0062:         icon: 'https://iconhoihoi.oops.jp/sozai/icon/166-planet/icon_3g_32.png',
0063:         color: '#FF0000',
0064:         weight: 1,
0065:         heading: 90
0066:     },

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

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

0280: /**
0281:  * Googleマップ関係
0282: */
0283: google.maps.event.addDomListener(window, 'load', function() {
0284:     var mapdiv = document.getElementById(gmapid);
0285:     var myOptions = {
0286:         zoomzoom,
0287:         centernew google.maps.LatLng(lat_centerlng_center),
0288:         mapTypeIdgoogle.maps.MapTypeId.maptype,
0289:         mapTypeControltrue,
0290:         scaleControltrue
0291:     };
0292:     gmap = new google.maps.Map(mapdivmyOptions);
0293: 
0294:     //初期化
0295:     initialize();
0296: 
0297:     //event.addListener
0298:     google.maps.event.addListener(gmap, 'click', function(e) {
0299:         onClickMap(e.latLng);
0300:     });
0301: });

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

解説:初期化処理

0231: /**
0232:  * 初期化処理
0233:  * @paramなし
0234:  * @returnなし
0235: */
0236: function initialize() {
0237:     //太陽の位置、マーカー
0238:     l_sun = new google.maps.LatLng(lat_sunlng_sun);
0239:     //描画済みのマーカーがあれば消去
0240:     if (markers['sun'] != null) {
0241:         markers['sun'].setMap(null);
0242:     }
0243:     //マーカーを描く
0244:     markers['sun'] = new google.maps.Marker({
0245:         positionl_sun,
0246:         iconnew google.maps.MarkerImage(
0247:             sun.icon,
0248:             new google.maps.Size(msizemsize),
0249:             new google.maps.Point(0, 0),
0250:             new google.maps.Point(msize / 2, msize / 2),  //位置調整
0251:         ),
0252:         mapgmap
0253:     });
0254: 
0255:     //地球の位置、マーカー
0256:     l_earth = new google.maps.LatLng(lat_earthlng_earth);
0257:     //描画済みのマーカーがあれば消去
0258:     if (markers['earth'] != null) {
0259:         markers['earth'].setMap(null);
0260:     }
0261:     //マーカーを描く
0262:     markers['earth'] = new google.maps.Marker({
0263:         positionl_earth,
0264:         iconnew google.maps.MarkerImage(
0265:             planets['earth'].icon,
0266:             new google.maps.Size(msizemsize),
0267:             new google.maps.Point(0, 0),
0268:             new google.maps.Point(msize / 2, msize / 2),  //位置調整
0269:         ),
0270:         mapgmap
0271:     });
0272: 
0273:     //距離計算
0274:     var distance = google.maps.geometry.spherical.computeDistanceBetween(markers['sun'].positionmarkers['earth'].position);
0275: 
0276:     //太陽系を描く
0277:     drawSolarSystem(markers['sun'].positiondistance);
0278: }

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

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

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

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

0121: /**
0122:  * 惑星の軌道を描く
0123:  * @param object latlng   太陽の位置(緯度・経度)
0124:  * @param double distance太陽=地球距離(メートル)
0125:  * @returnなし
0126: */
0127: function drawSolarSystem(latlngdistance) {
0128:     //惑星の数だけ繰り返す
0129:     for (let key in planets) {
0130:         radius = distance * planets[key].radius;    //軌道半長径
0131:         //描画済みの円があれば消去
0132:         if (orbits[key] != null) {
0133:             orbits[key].setMap(null);
0134:         }
0135:         //円を描く
0136:         orbits[key] = new google.maps.Circle({
0137:             mapgmap,
0138:             clickablefalse,
0139:             centerlatlng,                  //中心点
0140:             radiusradius,                  //半径(メートル)
0141:             fillOpacity: 0.0,                 //塗りつぶし透過度
0142:             strokeColorplanets[key].color,    //外周色 
0143:             strokeWeightplanets[key].weight   //外周太さ(ピクセル)
0144:         });
0145: 
0146:         //惑星マーカーを設置
0147:         if (key != 'earth') {
0148:             p_latlng = google.maps.geometry.spherical.computeOffset(latlngradiusplanets[key].heading);
0149:             //描画済みのマーカーがあれば消去
0150:             if (markers[key] != null) {
0151:                 markers[key].setMap(null);
0152:             }
0153:             //マーカーを描く
0154:             markers[key] = new google.maps.Marker({
0155:                 positionp_latlng,
0156:                 iconnew google.maps.MarkerImage(
0157:                     planets[key].icon,
0158:                     new google.maps.Size(msizemsize),
0159:                     new google.maps.Point(0, 0),
0160:                     new google.maps.Point(msize / 2, msize / 2),  //位置調整
0161:                 ),
0162:                 mapgmap
0163:             });
0164:         }
0165:     }
0166: }

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

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

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

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

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

0168: /**
0169:  * 各惑星の軌道、マーカーを消去する
0170:  * @paramなし
0171:  * @returnなし
0172: */
0173: function clearSolarSystem() {
0174:     for (let key in planets) {
0175:         //描画済みのマーカーがあり、地球以外なら消去
0176:         if ((markers[key] != null) && (key != 'earth')) {
0177:             markers[key].setMap(null);
0178:         }
0179:         //描画済みの円があれば消去
0180:         if (orbits[key] != null) {
0181:             orbits[key].setMap(null);
0182:         }
0183:     }
0184: };

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

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

0186: /**
0187:  * 地図クリック時の処理
0188:  * @param object lat_lngクリックされた座標
0189:  * @returnなし
0190: */
0191: function onClickMap(lat_lng) {
0192:     //太陽と地球のどちらが選ばれているか
0193:     var radios = document.myform.selector;
0194:     for (var i = 0; i < radios.lengthi++) {
0195:         if (radios[i].checked) {
0196:             selector = radios[i].value;
0197:             break;
0198:         }
0199:     }
0200:     var longitude = 'lng_' + selector;
0201:     var latitude  = 'lat_' + selector;
0202:     if (selector == 'sun') {
0203:         icon = sun.icon;
0204:     } else {
0205:         icon = planets['earth'].icon;
0206:     }
0207: 
0208:     //描画済みのマーカーがあれば消去
0209:     if (markers[selector] != null) {
0210:         markers[selector].setMap(null);
0211:     }
0212:     //マーカーを描く
0213:     markers[selector] = new google.maps.Marker({
0214:         positionlat_lng,
0215:         iconnew google.maps.MarkerImage(
0216:             icon,
0217:             new google.maps.Size(msizemsize),
0218:             new google.maps.Point(0, 0),
0219:             new google.maps.Point(msize / 2, msize / 2),  //位置調整
0220:         ),
0221:         mapgmap
0222:     });
0223: 
0224:     //距離計算
0225:     var distance = google.maps.geometry.spherical.computeDistanceBetween(markers['sun'].positionmarkers['earth'].position);
0226: 
0227:     //太陽系を描く
0228:     drawSolarSystem(markers['sun'].positiondistance);
0229: }

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

参考サイト

参考書籍

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