タイトルから、何の技術の話かわからないですね。
GoogleMapにナビゲーション機能ってありますよね、それをサイクリングでナビゲートしてもらおうというものです。
サイクリング中は、手が使えないので、音声ナビがあると便利です。
ですが、ポケストップを探すたびに、立ち止まってスマホを見るのは手間ですし、軽快ではありません。
そこで、(ポケストップに限りませんが)サイクリング前に、途中のチェックポイントを複数覚えておいて、チェックポイントまでの道のりをナビゲートしてもらって、到着したら、ポケストップのアイテムをもらったり、コンビニで休憩したのち、次のチェックポイントを目指す、というものです。
原理の説明:GoogleMapの機能
GoogleMapは、外部のアプリやブラウザから起動させることができます。
その際に、パラメータの指定によって、場所の表示だけでなく、出発地点と目的地を指定して、ナビゲーションを開始させることもできます。
ですので、まずはブラウザから、サイクリングで回りたいチェックポイントのリストを作って、GoogleMapを起動して次のチェックポイントまでナビゲーションしてもらいます。
チェックポイントまで到着したら、またブラウザを立ち上げて、今度は次のチェックポイントを指定してGoogleMapを起動させる、これを最後の目的地まで繰り返すわけです。
GoogleMapのナビゲーション機能
ナビゲーション中は、以下のことを適宜音声で教えてくれます。
・予定通りの道を進んでいるかどうか
・次の曲がり角まで何メートルか
・今曲がるべき曲がり角か
・予定の道を外れたか
・目的地の近くに来たか
・目的地に着いたか
上記は、ナビを開始すると、設定が選べるようになり、「詳しい音声案内」のスイッチをOnにした場合です。Offの場合はもうちょっと少ない気がします。
もろもろGitHubに上げておきました。
poruruba/orientation_navigator
https://github.com/poruruba/orientation_navigator
画面説明
ブラウザを起動するとこんな感じの画面が表示されます。
まずは、チェックポイントを追加します。タブ「チェックポイント」を選択し、チェックポイントを追加します。
GoogleMapがはめ込まれて表示されるので、出発地点を選択します。
さらに、同じように次のチェックポイントを追加します。
経由地っていうチェックボックスがあります。通常は次のチェックポイントに到着するとGoogleMapのナビゲーションが終わってしまうのですが、経由地は次のチェックポイントまでの途中の経由地であって、経由地を通過しただけでは、GoogleMapのナビゲーションは終わらないようにしています。
オリエンテーションタブを選択すると、マーキングされているのがわかります。
これで準備完了です。
さっそく、「オリエンテーション開始」ボタンを押下してみましょう。
そうすると、こんな感じでGoogleMapが立ち上がり、ナビゲーション開始待ちとなります。
ちなみに、PCのChromeブラウザからの画面ですが、Androidから使うと、ブラウザのGoogleMapか、ネイティブのGoogleMapアプリ、どちらを起動するかの選択肢が出てきます。もちろん、ネイティブのGoogleMapアプリの方がナビゲーションとしては使い勝手が良いです。
あとは、開始してしまえば、いつものナビゲーションが始まります。
イヤホンで、GoogleMusic(Youtube Music)でも聞きながら、サイクリングしましょう。
チェックポイントに到着したら、もう一度ブラウザに戻りましょう。
「チェックポイントに到着しましたか?」ボタンを押下すると、次のチェックポイントに出発のボタンに代わりますので、押下すると、またGoogleMapが立ち上がります。今度は、1つ目のチェックポイントから、2つ目のチェックポイントへのナビゲーションです。
おおよそ、イメージはつかめましたでしょうか?
マイスポット機能
毎度毎度、場所を選択するのはめんどうです。特に家の周りはいつものコースを決めていますが、毎度ポケストップを指定するのは面倒です。
そこで、あらかじめよくいくスポットをマイスポット機能として登録しておけば、それを選択するだけで、チェックポイントに追加されるようになります。
マイスポットのタブを選択して、登録します。
そうすると、こんな感じで、チェックポイント登録する際に、マイスポットから選択することができます。
サーバ同期
実は、チェックポイントを追加したり、マイスポットを追加したりしたら、サーバ側にデータを保持するようにしています。ですので、ブラウザを立ち上げなおしても、以前の状態が復元されるようにしています。また、ナビゲーション中にチェックポイントに到達したりした時もサーバ側に同期するようにしています。
とはいってもクライアント・サーバいずれもかなり手抜きしています。
クライアント側は、Vueのwatchを使って、対象のデータが変更されたら、サーバに一括アップロードしているだけです。
取得も、ブラウザでの起動時だけです。
GoogleMap起動のURL生成
大事なところをピックアップしました。
travelmode、origin、destination、waypointsを指定しているのがわかります。
travelmode
歩きの移動か、車の移動か、電車の移動かを指定します。自転車がありますが、日本では使えないようです。
origin
出発地点です。ブラウザで設定したチェックポイントに相当します。経由地ではありません。
destination
これもブラウザで設定したチェックポイントですが、originのチェックポイントの次のチェックポイントです。
waypoints
これが経由地です。
// GoogleMap起動のパラメータを生成varparams="";varorigin=this.checkpoints[this.origin_index];params+="&travelmode="+this.travelmode;params+="&origin="+encodeURIComponent(origin.lat+','+origin.lng);if(destination_index<=(this.checkpoints.length-1)){vardestination=this.checkpoints[destination_index];params+="&destination="+encodeURIComponent(destination.lat+','+destination.lng);}if((this.origin_index+1)<destination_index){varwaypoints="";for(vari=(this.origin_index+1);i<destination_index;i++){if(i!=(this.origin_index+1))waypoints+='|';waypoints+=this.checkpoints[i].lat+','+this.checkpoints[i].lng;}params+="&waypoints="+encodeURIComponent(waypoints);}varhref='https://www.google.com/maps/dir/?api=1'+params;console.log(href);this.destination_completed=false;// GoogleMapを起動window.open(href,'_blank');
詳しくは以下を参照してください。
GoolgeMap Developers Guide Univarsal cross-platform syntax
https://developers.google.com/maps/documentation/urls/guide?hl=ja#directions-action
ソース一式
Javascriptのソースです。
'use strict';//var vConsole = new VConsole();constdefault_lat=35.465878;constdefault_lng=139.622329;constbase_url="http://localhost:10080";varvue_options={el:"#top",data:{progress_title:'',// for progress-dialogorigin_index:0,// 出発のインデックスdestination_completed:true,// チェックポイントに到着したかどうかcheckpoints:[],// チェックポイントのリストmap_markers:[],// Map1に配置のマーカmyspots:[],// マイスポットのリストdialog_params:{},// モーダルダイアログの入出力パラメタmap2_markers:[],// Map2に配置のマーカdefault_latlng:newgoogle.maps.LatLng(default_lat,default_lng),// デフォルトのロケーション(現在地に上書き)travelmode:'walking',// GoogleMapに指定するtravelmode},computed:{// ボタンに表示するテキストorientation_text:function(){if(!this.destination_completed)return'チェックポイントに到着しましたか?';if(this.origin_index==0)return'オリエンテーション開始';elseif(this.origin_index>=(this.checkpoints.length-1))return'最終目的地に到着しました。';elsereturn'次のチェックポイントに出発';}},watch:{checkpoints:function(){// Mapに配置のマーカを再設定for(vari=0;i<this.map_markers.length;i++)this.map_markers[i].setMap(null);this.map_markers=[];for(vari=0;i<this.checkpoints.length;i++){varlatlng=newgoogle.maps.LatLng(this.checkpoints[i].lat,this.checkpoints[i].lng);varmopts={position:latlng,map:this.map,label:String(i+1)};varmarker=newgoogle.maps.Marker(mopts);this.map_markers.push(marker);}try{// サーバに同期update_data('checkpoints',this.checkpoints);}catch(error){this.toast_show("サーバにデータをアップロードできませんでした。");};},myspots:function(){try{// サーバに同期update_data('myspots',this.myspots);}catch(error){this.toast_show("サーバにデータをアップロードできませんでした。");};},origin_index:function(){try{// サーバに同期update_data('orientation',{origin_index:this.origin_index,destination_completed:this.destination_completed});}catch(error){this.toast_show("サーバにデータをアップロードできませんでした。");};},destination_completed:function(){try{// サーバに同期update_data('orientation',{origin_index:this.origin_index,destination_completed:this.destination_completed});}catch(error){this.toast_show("サーバにデータをアップロードできませんでした。");};},},methods:{// デフォルトのロケーションに移動map_goto_current_location:function(){varlatlng=this.default_latlng;this.map.setCenter(latlng);},// オリエンテーションタブ選択時にデフォルトのロケーションまたは出発位置に移動orientation_update_view:function(){varlatlng=this.default_latlng;if(this.checkpoints.length>0)latlng=newgoogle.maps.LatLng(this.checkpoints[this.origin_index].lat,this.checkpoints[this.origin_index].lng);this.map.setCenter(latlng);},// オリエンテーションを指定位置からリスタートorientation_restart:function(index){if(index<0)if(!window.confirm('本当に最初から初めてもいいですか?'))return;this.origin_index=(index<0)?0:index;this.destination_completed=true;this.orientation_next();},// 次の目的地(経由地を除く)を取得get_next_destination:function(){vardestination_index=this.origin_index+1;for(;destination_index<this.checkpoints.length;destination_index++)if(!this.checkpoints[destination_index].waypoint)break;if(destination_index>=this.checkpoints.length)destination_index=this.checkpoints.length-1;returndestination_index;},// 次へのボタンを押下orientation_next:function(){if(this.checkpoints.length==0){alert('チェックポイントを追加してください。');return;}elseif(this.checkpoints.length==1){alert('次のチェックポイントを追加してください。');return;}if(this.origin_index>=(this.checkpoints.length-1)){alert('すでに目的地に到達しています。');return;}vardestination_index=this.get_next_destination();if((destination_index-(this.origin_index+1))>9){alert('経由地の数が多すぎます。(9以下)');return;}if(!this.destination_completed){// チェックポイントに到達this.origin_index=destination_index;this.destination_completed=true;// 目的位置に到達if(destination_index>=(this.checkpoints.length-1))this.dialog_open('#orientation_complete_dialog');return;}// GoogleMap起動のパラメータを生成varparams="";varorigin=this.checkpoints[this.origin_index];params+="&travelmode="+this.travelmode;params+="&origin="+encodeURIComponent(origin.lat+','+origin.lng);if(destination_index<=(this.checkpoints.length-1)){vardestination=this.checkpoints[destination_index];params+="&destination="+encodeURIComponent(destination.lat+','+destination.lng);}if((this.origin_index+1)<destination_index){varwaypoints="";for(vari=(this.origin_index+1);i<destination_index;i++){if(i!=(this.origin_index+1))waypoints+='|';waypoints+=this.checkpoints[i].lat+','+this.checkpoints[i].lng;}params+="&waypoints="+encodeURIComponent(waypoints);}varhref='https://www.google.com/maps/dir/?api=1'+params;console.log(href);this.destination_completed=false;// GoogleMapを起動window.open(href,'_blank');},// Map2のマーカをクリアし、指定場所に移動map2_cleanup:function(latlng){for(vari=0;i<this.map2_markers.length;i++)this.map2_markers[i].setMap(null);this.map2_markers=[];if(this.map2_default_marker){this.map2_default_marker.setMap(null);this.map2_default_marker=null;}this.map2.setCenter(latlng);},// モーダルダイアログの結果処理dialog_submit:function(){if(this.dialog_params.title=='マイスポットの追加'){varlocation=this.map2.getCenter();varname=this.dialog_params.name;this.myspots.push({name,lat:location.lat(),lng:location.lng()});}elseif(this.dialog_params.title=='チェックポイントの追加'){varlocation=this.map2.getCenter();varname=this.dialog_params.name;this.checkpoints.push({name,lat:location.lat(),lng:location.lng()});}elseif(this.dialog_params.title=='マイスポットの変更'){varlocation=this.map2.getCenter();this.myspots[this.dialog_params.index].lat=location.lat();this.myspots[this.dialog_params.index].lng=location.lng();}elseif(this.dialog_params.title=='チェックポイントの変更'){varlocation=this.map2.getCenter();this.checkpoints[this.dialog_params.index].lat=location.lat();this.checkpoints[this.dialog_params.index].lng=location.lng();}this.dialog_close('#select_location_dialog');},// マイスポットの追加(地図から)のためのモーダルダイアログ表示do_myspot_append:function(){this.map2_cleanup(this.default_latlng);this.map2_default_marker=newgoogle.maps.Marker({position:this.default_latlng,map:this.map2,});this.dialog_params={title:'マイスポットの追加',is_input_name:true,is_input_submit:true,};this.dialog_open('#select_location_dialog');},// マイスポットの削除do_myspot_delete:function(index){if(!window.confirm('本当に削除していいですか?'))return;Vue.delete(this.myspots,index);},// マイスポットの名前変更do_myspot_change_name:function(index){varname=window.prompt('新しい名前',this.myspots[index].name);if(!name)return;this.myspots[index].name=name;},// マイスポットのロケーション変更do_myspot_change_location:function(index){varlatlng=newgoogle.maps.LatLng(this.myspots[index].lat,this.myspots[index].lng);this.map2_cleanup(latlng);this.map2_default_marker=newgoogle.maps.Marker({position:latlng,map:this.map2,});this.dialog_params={title:'マイスポットの変更',index:index,is_input_submit:true,};this.dialog_open('#select_location_dialog');},// チェックポイントリストのリセットdo_checkpoints_reset:function(){if(!window.confirm('本当にリセットしていいですか?'))return;this.origin_index=0;this.destination_completed=true;this.checkpoints=[];},// チェックポイントの追加(マイスポットから)のためのモーダルダイアログ表示do_checkpoint_append_myspot:function(){if(this.myspots.length==0){alert('マイスポットが登録されていません。');return;}this.map2_cleanup(this.default_latlng);this.dialog_params={title:'チェックポイントの追加(マイスポット)',};var_this=this;for(vari=0;i<this.myspots.length;i++){varmopts={position:newgoogle.maps.LatLng(this.myspots[i].lat,this.myspots[i].lng),map:this.map2,};varmarker=newgoogle.maps.Marker(mopts);this.map2_markers.push(marker);marker.addListener('click',function(e){for(vari=0;i<_this.map2_markers.length;i++){if(_this.map2_markers[i]==this){_this.checkpoints.push(_this.myspots[i]);_this.dialog_close('#select_location_dialog');return;}}});}this.dialog_open('#select_location_dialog');},// チェックポイント追加(地図から)のためのモーダルダイアログ表示do_checkpoint_append:function(){this.map2_cleanup(this.default_latlng);this.map2_default_marker=newgoogle.maps.Marker({position:this.default_latlng,map:this.map2,});this.dialog_params={title:'チェックポイントの追加',is_input_name:true,is_input_submit:true,name:(this.checkpoints.length==0)?'現在地':'',};this.dialog_open('#select_location_dialog');},// チェックポイントの削除do_checkpoint_delete:function(index){if(!window.confirm('本当に削除していいですか?'))return;Vue.delete(this.checkpoints,index);},// チェックポイントの名前変更do_checkpoint_change_name:function(index){varname=window.prompt('新しい名前',this.checkpoints[index].name);if(!name)return;this.checkpoints[index].name=name;},// チェックポイントのロケーション変更do_checkpoint_change_location:function(index){varlatlng=newgoogle.maps.LatLng(this.checkpoints[index].lat,this.checkpoints[index].lng);this.map2_cleanup(latlng);this.map2_default_marker=newgoogle.maps.Marker({position:latlng,map:this.map2,});this.dialog_params={title:'チェックポイントの変更',index:index,is_input_submit:true,};this.dialog_open('#select_location_dialog');},// チェックポイントの順番変更do_checkpoint_change_index:function(index,event){varnewIndex=event.target.selectedIndex;vartemp=this.checkpoints[index];Vue.set(this.checkpoints,index,this.checkpoints[newIndex]);Vue.set(this.checkpoints,newIndex,temp);},},created:function(){},mounted:function(){proc_load();// 現在地情報の取得navigator.geolocation.getCurrentPosition((pos)=>{this.default_latlng=newgoogle.maps.LatLng(pos.coords.latitude,pos.coords.longitude);this.map_goto_current_location();},(error)=>{this.toast_show("現在地を取得できませんでした。");});// Mapの生成varmyOptions={zoom:15,center:this.default_latlng,mapTypeId:google.maps.MapTypeId.ROADMAP,mapTypeControl:false,streetViewControl:false,};varcanvas=$('#map_canvas')[0];this.map=newgoogle.maps.Map(canvas,myOptions);// Map2(モーダルダイアログ用)の生成varcanvas2=$('#map_canvas2')[0];this.map2=newgoogle.maps.Map(canvas2,myOptions);google.maps.event.addListener(this.map2,'center_changed',()=>{if(!this.map2_default_marker)return;varlocation=this.map2.getCenter();this.map2_default_marker.setPosition(location);});// サーバ保持データの取得get_data('myspots').then(data=>{this.myspots=data;returnget_data('checkpoints');}).then(data=>{this.checkpoints=data;returnget_data('orientation');}).then(data=>{if(data.origin_index!=undefined)this.origin_index=data.origin_index;if(data.destination_completed!=undefined)this.destination_completed=data.destination_completed;}).catch(error=>{this.toast_show("サーバからデータを取得できませんでした。");});}};vue_add_methods(vue_options,methods_bootstrap);vue_add_components(vue_options,components_bootstrap);varvue=newVue(vue_options);functiondo_post(url,body){constheaders=newHeaders({"Content-Type":"application/json; charset=utf-8"});returnfetch(newURL(url).toString(),{method:'POST',body:JSON.stringify(body),headers:headers}).then((response)=>{if(!response.ok)throw'status is not 200';returnresponse.json();});}asyncfunctionget_data(type){returndo_post(base_url+'/get-data',{type:type}).then(json=>{if(json.status!='OK')throw"post failed";returnjson.result.data;});}asyncfunctionupdate_data(type,data){varbody={type:type,data:data,}returndo_post(base_url+'/update-data',body).then(json=>{if(json.status!='OK')throw"post failed";});}
次はHTMLです。
<!DOCTYPE html><htmllang="ja"><head><metahttp-equiv="Content-Type"content="text/html; charset=utf-8"/><metahttp-equiv="Content-Security-Policy"content="default-src * data: gap: https://ssl.gstatic.com 'unsafe-eval' 'unsafe-inline'; style-src * 'unsafe-inline'; media-src *; img-src * data: content: blob:;"><metaname="format-detection"content="telephone=no"><metaname="msapplication-tap-highlight"content="no"><metaname="apple-mobile-web-app-capable"content="yes"/><metaname="viewport"content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width"><!-- jQuery (necessary for Bootstrap's JavaScript plugins) --><script src="https://code.jquery.com/jquery-1.12.4.min.js"integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ"crossorigin="anonymous"></script><!-- Latest compiled and minified CSS --><linkrel="stylesheet"href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu"crossorigin="anonymous"><!-- Optional theme --><linkrel="stylesheet"href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap-theme.min.css"integrity="sha384-6pzBo3FDv/PJ8r2KRkGHifhEocL+1X2rVCTTkUfGk7/0pbek5mMa1upzvWbrUbOZ"crossorigin="anonymous"><!-- Latest compiled and minified JavaScript --><script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd"crossorigin="anonymous"></script><title>オリエンテーション ナビゲータ</title><linkrel="stylesheet"href="css/start.css"><script src="js/methods_bootstrap.js"></script><script src="js/components_bootstrap.js"></script><script src="js/vue_utils.js"></script><script src="dist/js/vconsole.min.js"></script><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><script src="https://cdn.jsdelivr.net/npm/js-cookie@2/src/js.cookie.min.js"></script><linkrel="stylesheet"href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css"><script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script><script type="text/javascript"src="//maps.google.com/maps/api/js?key=【GoogleAPIキー】"></script></head><body><divid="top"class="container"><h1>オリエンテーション ナビゲータ</h1><ulclass="nav nav-tabs"><lirole="presentation"class="active"><ahref="#oriatation"v-on:click="orientation_update_view"data-toggle="tab">オリエンテーション</a></li><lirole="presentation"><ahref="#checkpoint"data-toggle="tab">チェックポイント</a></li><lirole="presentation"><ahref="#myspot"data-toggle="tab">マイスポット</a></li></ul><divclass="tab-content"><divid="oriatation"class="tab-pane fade in active"><br><spanclass="form-inline"><selectclass="form-control"v-model="travelmode"><optionvalue="walking">walking</option><optionvalue="bicycling">bicycling</option><optionvalue="driving">driving</option><optionvalue="transit">transit</option></select></span><divclass="btn-group"><buttonclass="btn btn-primary"v-on:click="orientation_next">{{orientation_text}}</button><buttontype="button"class="btn btn-primary dropdown-toggle"data-toggle="dropdown"><spanclass="caret"></span></button><ulclass="dropdown-menu"><li><av-on:click="orientation_restart(-1)">最初から再開</a></li><li><av-on:click="orientation_restart(origin_index)">今のチェックポイントを再開</a></li></ul></div><spanv-if="checkpoints.length >= 2"><br><br><label>現在地:</label>{{origin_index + 1}} {{checkpoints[origin_index].name}},
<label>目的地:</label>{{get_next_destination() + 1}} {{checkpoints[get_next_destination()].name}}
</span><buttonclass="btn btn-default btn-xs pull-right"v-on:click="map_goto_current_location">現在地へ</button><br><divclass="row"id="map_canvas"style="margin: 15px; height:600px"></div></div><divid="checkpoint"class="tab-pane fade in"><br><buttonclass="btn btn-primary"v-on:click="do_checkpoints_reset">チェックポイントのリセット</button><tableclass="table table-striped"><thead><tr><th>#</th><th>名前</th><th>経由地</th><th>編集</th></tr></thead><tbody><trv-for="(point, index) in checkpoints"><tdwidth="1px"><divclass="form-inline"><selectv-bind:value="index"v-on:change="do_checkpoint_change_index(index, $event)"><optionv-for="(point2, index2) in checkpoints"v-bind:value="index2"v-bind:selected="index==index2">{{index2 + 1}}</option></select></div></td><td><buttonclass="btn btn-default btn-xs"v-on:click="do_checkpoint_delete(index)">削除</button> {{point.name}}
</td><td><inputv-if="index!=0 && index!=(checkpoints.length-1)"type="checkbox"v-model="point.waypoint"></td><td><divclass="btn-group"><buttonclass="btn btn-default btn-sm"v-on:click="do_checkpoint_change_name(index)">名前</button><buttonclass="btn btn-default btn-sm"v-on:click="do_checkpoint_change_location(index)">場所</button></div></td></tr><tr><td></td><td><divclass="btn-group"><buttonclass="btn btn-default btn-sm"v-on:click="do_checkpoint_append">チェックポイント追加</button><buttontype="button"class="btn btn-default btn-sm dropdown-toggle"data-toggle="dropdown"><spanclass="caret"></span></button><ulclass="dropdown-menu"><li><av-on:click="do_checkpoint_append_myspot">マイスポットから追加</a></li></ul></div></td><td></td><td></td></tr></tbody></table></div><divid="myspot"class="tab-pane fade in"><br><tableclass="table table-striped"><thead><tr><th>#</th><th>名前</th><th>緯度</th><th>経度</th><th>編集</th></tr></thead><tbody><trv-for="(spot, index) in myspots"><tdwidth="1px">{{index + 1}}</td><td><buttonclass="btn btn-default btn-xs"v-on:click="do_myspot_delete(index)">削除</button> {{spot.name}}</td><td>{{spot.lat.toFixed(7)}}</td><td>{{spot.lng.toFixed(7)}}</td><td><divclass="btn-group"><buttonclass="btn btn-default btn-sm"v-on:click="do_myspot_change_name(index)">名前</button><buttonclass="btn btn-default btn-sm"v-on:click="do_myspot_change_location(index)">場所</button></div></td></tr><tr><td></td><td><buttonclass="btn btn-default btn-sm"v-on:click="do_myspot_append">地図から追加</button></td><td></td><td></td><td></td></tr></tbody></table></div><br><br></div><modal-dialogsize="lg"id="orientation_complete_dialog"><divslot="content"><divclass="modal-header"><h4class="modal-title">オリエンテーション達成</h4></div><divclass="modal-body"><center>オリエンテーション達成です。おめでとうございます。<br><imgsrc="img/goal_figure.png"></center></div><divclass="modal-footer"><buttonclass="btn btn-default"v-on:click="dialog_close('#orientation_complete_dialog')">閉じる</button></div></div></modal-dialog><modal-dialogsize="lg"id="select_location_dialog"><divslot="content"><divclass="modal-header"><h4class="modal-title">{{dialog_params.title}}</h4></div><divclass="modal-body"><divclass="form-inline"><buttonclass="btn btn-default"v-on:click="dialog_submit"v-if="dialog_params.is_input_submit">この場所にする</button><spanv-if="dialog_params.is_input_name"><label>名前</label><inputtype="text"class="form-control"v-model="dialog_params.name"></span></div><divclass="row"id="map_canvas2"style="margin: 20px; height:300px"></div></div><divclass="modal-footer"><buttonclass="btn btn-default"v-on:click="dialog_close('#select_location_dialog')">キャンセル</button></div></div></modal-dialog><!-- for progress-dialog --><progress-dialogv-bind:title="progress_title"></progress-dialog></div><script src="js/start.js"></script></body>
もう長すぎてわけわかんないですよね。。。
その他ユーティリティのファイル等含めて、GitHubに上げています。
セットアップ:GoogleMap API利用の準備
GoogleMapを利用するには、GoogleからAPIキーを払い出してもらう必要があります。
以下のサイトの通りに実施すれば、特に問題はなかったです。
Get Started with Google Maps Platform
https://developers.google.com/maps/gmp-get-started?hl=ja
最後に、API Keyを生成するのですが、Web APIsのMaps Embed APIを採用しました。
セットアップ:サーバの展開
以下のGitHubから一式ダウンロードしておきます。
poruruba/orientation_navigator
https://github.com/poruruba/orientation_navigator
そして、以下の通りに実行します。
unzip orientation_navigator.zip
cd orientation_navigator
npm install
mkdir data
node app.js
以下、修正が必要です。
public/start.js
7行目のあたり
const base_url = "http://localhost:10080";
上記を立ち上げたサーバのURLを指定します。
public/index.html
34行目あたり
<script type="text/javascript" src="//maps.google.com/maps/api/js?key=【GoogleAPIキー】"></script>
また、HTML5の現在地情報取得機能を使っているのですが、それを利用するには、HTTPSである必要があります。
mkdir certs
このディレクトリに、SSL証明書類を配置しましょう。
以下が参考になります。
SSL証明書を取得しよう
以上