« 7月20日の行動 | メイン | カレーそぼろ丼 »

2005年07月20日

[技術] Ajaxサンプル with Google Maps

Ajaxサンプルの第二弾が完成した。第一弾を拡張して、番号を2桁入力した時点で、マッチする山情報をプルダウンして、マウスでクリックもしくはカーソル移動+リターンで確定し、その山のGoogle Mapsを表示します。このサンプルを作成するにあたり参考にしたのは、map.rails2u.comです。(つぅーか、JavaScriptはかなりパクリました)

Google Mapsの方は、マップが表示されるようになっていますが、マップ右上の「サテライト」をクリックすると人工衛星写真に切り替わります。ちなみに地図はマウスでがんがんスクロールしますので、遊んでみてください。(ちゅーか、こっちがAjaxの本家本元のお手本ですね)

さて、このサンプルプログラムをつくるのにははっきり言って苦労しました。参考にしたmap.rails2u.comの方は、Ruby on Railsを使ってシャカシャカと作ってしまったようなのですが、私の方はJavaScriptがよくわからんところで、いきなりフレームワークでは内部的なやり取りがどうなっているのか完全にブラックボックスで、フレームワーク上での指定がぴんと来ません。そこで、ライブラリととして使われているJavaScriptを解読したために時間を取られました。

また、位置情報系の知識もなかったので、緯度経度でさらに泥沼に嵌ってしまい、Google Mapsと連携しても、全然違う場所が表示されてしまい。はて? どうしたものか、というつらい時間が結構続きました。

ということで、今回のサンプル作成で、わかったことを備忘録として以下に書いておきます。

■Ajax用 JavaScriptライブラリ
 Ruby on RailsやCatalystなどのフレームワークで、Ajaxを実現するときに使用しているJavaScriptライブラリを以下のサイトより、入手する。バージョンは1803。ちなみにダウンロードできなかったので、ブラウザにソースを表示してコピペで自分のサイトに置いた。

http://dev.rubyonrails.com/browser/spinoffs/scriptaculous/?rev=1803

ファイルは全部で、prototype.js, controls.js, effects.js, dragdrop.jsの4つ。今回は、dragdrop.jsを除く3つのファイルをpoint2.htmlでインクルードした。htmlのヘッダ部に以下を書きます。

<script src="prototype.js" type="text/javascript"></script>
<script src="controls.js" type="text/javascript"></script>
<script src="effects.js" type="text/javascript"></script>

で、入力候補をメニューにブルダウン表示して、マウスもしくはカーソルキーで選択できるAjaxインターフェースを提供するクラスが、controls.jsにあるAjax.Autocompleterクラス。このクラスのコンストラクターは、引数として、(id, update, url, {options})の4つをとる。

id -> 入力を監視するINPUTタグのid属性を指定
update -> 候補選択メニューを挿入する場所(タグ)のid属性を指定
url -> XmlHTTPRequestとやり取りするサーバ側CGIのURLを指定
{options} -> オプションを指定 ({tokens:new Array(',','\n')}のように指定できるらしい)

point2.html内では、以下のように記述しています。

<script type="text/javascript">
var auto = new Ajax.Autocompleter('pointid',
                       'pointid_menu',
                       'point2.cgi',
                       {});
auto.select_entry = select_entry;
</script>

で、入力を監視する属性id="pointid"と候補メニューを表示する属性id="pointid_menu"は以下のように記述します。

<p>番号: <input type="text" autocomplete="off" class="inputText" name="pointid" id="pointid" size="40" />
<input type="button" value="国土地理院の地図表示" onClick="watchizu();">
</p>
<div class="auto_complete" id="pointid_menu"></div>

そして、サーバ側に用意したCGI(point2.cgi)はこちら。このCGIでは、以下のようなXHTML形式で検索結果をブラウザに返します。

<ul class="pointdata">
 <li class="pointid">
  <span class="pointid">102</span> 
  <span class="pointname">瑞牆山</span><br /> 
  (緯度: <span class="ido">35.53.36</span>
   経度: <span class="keido">138.35.31</span> 
   標高: <span class="height">2230</span> ) 
 </li>
 <li class="pointid">
  <span class="pointid">101</span> 
  <span class="pointname">横尾山</span><br />
  (緯度: <span class="ido">35.55.08</span>
   経度: <span class="keido">138.31.16</span>
   標高: <span class="height">1818</span>)
  </li>
 <li class="pointid">
  <span class="pointid">100</span> 
  <span class="pointname">甲武信ヶ岳</span><br />
  (緯度:<span class="ido">35.54.32</span> 
   経度:<span class="keido">138.43.44</span>
   標高:<span class="height">2475</span>) 
  </li>
</ul>

そして、表示する候補メニューに見栄えは以下のCSSで調整します。

div.auto_complete {
  position: absolute;
  background-color: #fff;
  border:1px solid #888;
  margin:0px;
  padding:0px;
  z-index:100;
}
ul.pointdata  {
  list-style-type: none;
  margin:0px;
  padding:0px;
}
// 候補メニュー中、フォーカスがあたっている候補を色反転する
ul.pointdata li.selected {
  background-color: #B2BEFF;
}
li.pointid {
  list-style-type: none;
  display:block;
  margin:0;
  padding:2px;
  height:45px;
}
span.pointid{
  font-weight:bold;
  color:#555;
}
span.pointname{
  color:#555;
}

実際にブラウザ上でユーザが候補を選択すると、Ajax.Autocompleter.select_entry()がコールされます。そこで、このselect_entry()関数を自前で作成して、オーバーライドすることで選択された候補を取り込み、ブラウザに値を反映させます。point2.htmlでは、関数select_entry()は以下のように定義し、Autocompleterのインスタンスを生成したところで、var auto = Ajax.Autocompleter(....); auto.select_entry = select_entryとしています。

function select_entry() {
    this.active = false;
    var result = Element.collectTextNodesSelectClassnames(this.get_current_entry(), ['pointid','pointname','ido','keido','height']);
    if(result['pointid'])
      $('pointid').value = result['pointid'];
    if(result['pointname'])
      $('pointname').value = result['pointname'];
    if(result['ido'])   //name="ido"
      $('ido').value = result['ido'];
    if(result['keido']) //name="keido"
      $('keido').value = result['keido'];
    if(result['height'])
      $('height').value = result['height'];
    // Google Maps
    if(result['keido'] && result['ido']){
      var decido = ToDecimalNotation(result['ido']);
      var deckeido = ToDecimalNotation(result['keido']);
      gmap.centerAndZoom(GPointTokyo(parseFloat(deckeido),parseFloat(decido)),4 );
      Element.show('gmap');
    }
    //
    this.element.focus();
};
Element.collectTextNodesSelectClassnames = function(element, classnames) {
  var children = $(element).childNodes;
  var result = new Object();
  for (var i = 0; i < classnames.length; i++) {
    for (var j = 0; j < children.length; j++) {
      if((children[j].className == classnames[i]) && children[j].hasChildNodes()){
        result[classnames[i]] = children[j].childNodes[0].nodeValue;
        break;
      }
    }
  }
  return result;
}

このselect_etnry()では、候補が決定され、緯度経度が取得できたところで、Google MapsのAPIを利用して、緯度経度に該当する地図も表示させています。つぎは、Google MapsのAPIと緯度経度について言及します。

■Google Maps APIと緯度経度
GPSなどの位置情報に関する知識がほとんどない状態で、このサンプルを作成開始したために緯度経度の問題でかなりなやみました。

まず、第一に緯度経度が世界で統一されていないということでした。日本では、従来、「日本測地系(TOKYO)」といわれる緯度経度を利用し、世界標準の「世界測地系(WGS-84)とは、同じ緯度経度でも指し示す実際の場所が異なるということでした。

しかし、日本では法律を改正して、2004年4月より従来の日本測地系から世界測地系を使うようにしたので、国土地理院が提供データなどは世界測地系に変換されている。

Google Maps APIは基本的に世界測地系を採用していいるが、日本地図(ゼンリンの地図らしい)だけは日本測地系を採用している。
05年12月1日より、Google Mapsの日本地図データも世界測地系に変更になりました。

まず、この即地系の対応で混乱してしまい、国土地理院からとってきた山の緯度経度データをどう変換して、Google Maps APIに食わせるたらいいのかで、混乱しました。

そして、その混乱に拍車をかけたのが、緯度経度の表記法が3種類あるということでした。度分秒式(例 23° 27′ 30")と秒十進式(例 23° 27.500′)と分秒十進式(例 23.45833°)があるということです。やられました。


で、結論ですが、Google Maps APIを使って、日本地図を表示させるには、日本測地系の緯度経度を分秒十進式にしなければいけないということでした。このことが判明するまで、実験したり、検索しまっくたりで、えらい苦労しました。

このあたりの変換は、point2.htmlのToDecimalNotation(ddmmss)やGPointTokyo(lng,lat)関数を参照してください。世界測地系緯度経度(度分秒表記)→分秒十進表記→日本測地系緯度経度→Google Maps APIという手順を踏んでいます。

で、国土地理院の度分秒式のデータを、分秒十進式に変換して、Google Maps APIに与えてやります。
このあたりの変換は、point2.htmlのToDecimalNotation(ddmmss)関数を参照してください。世界測地系緯度経度(度分秒表記)→分秒十進表記→Google Maps APIという手順を踏んでいます。

ちなみに、度分秒表記を分秒十進表記への変換式は以下になります。

分秒十進緯度経度 = 度 + (分 / 60)  + (秒 / 3600)

ということで、AjaxのライブラリのAutocompleter(この中で、XmlHTTPRequestを使っています)の使い方とGoogle Maps APIとの関連での緯度経度についてのメモでした。

投稿者 nekobara : 2005年07月20日 16:46


トラックバック

このエントリーのトラックバックURL:
http://www.ishihara.ne.jp/mt/mt-tb.cgi/428

コメント

サーバーにどのファイルを置いたら、
動くようになりますか?
何のファイルが必要でしょうか?
分からないので、お願いいたします。

投稿者 うえさる : 2005年09月09日 14:57

うえさるさん、こんにちは。

http://www.ishihara.ne.jp/sample/
に関連するファイルはすべてありますので、point2.htmlのソースをみて関連するファイル取得してくさださい。

ただし、GoogleMapsAPIにアクセスするための認証KEYはご自分のサイト用のものを取得する必要があります。

point2.hmtlの以下の部分になります。


<script src="http://maps.google.com/maps?file=api&v=1&key=ABQIAAAAvzossUap8XaKMoKinhe-xBQUw99fbUdSuXf4Ut-oWBFCCECKeRSinqVO7iOgfts_wJQVF5L3K-ia2Q" type="text/javascript"></script>

投稿者 nekobara : 2005年09月10日 21:05