Google Maps API V3 と Gears Geolocation API 使って Android のブラウザで現在位置情報を取得する

このエントリーをはてなブックマークに追加
はてなブックマーク - Google Maps API V3 と Gears Geolocation API 使って Android のブラウザで現在位置情報を取得する
Share on Facebook
[`tweetmeme` not found]

先日のGoogle Developer Day 2009 にて、GoogleさんからAndroid携帯である GDD Phone をいただいたので、早速Androidライフを楽しんでる毎日です。素敵なオモチャをありがとうございます!

ネイティブアプリの地図でGoogle Latitudeをいぢりたおしたりしてるんですが、地図大好きっ子としては自分で位置情報を活用したいところ。

そんな折、ジークルーの佐々木さんから

@shinagaki 自分が今日どこを歩いたかが履歴でMapに表示されるAndroidアプリって知らない?

って遠まわしに作れって言われた気がしたので、Android界隈の位置情報について調べてみる。

Androidで位置情報を取得するにはどうすんの?

調べてみたけど、ネイティブアプリでの情報しかなくて、実装が楽にできそうなブラウザでのAPI動作状況についてのサンプル例がなかった。

ネイティブアプリでは、Maps External Library – Google Projects for Android に情報あるから、ネイティブアプリ作る人は見るといいよ。僕も、あとで見る。

Androidの搭載しているブラウザに、iPhone OS 3.0 のSafariブラウザみたくW3CのGeolocation APIをサポートして、ブラウザからGPS位置情報を取得できることを期待してたんですが、結果から言うとダメ。

ですが!Androidのブラウザ「Chrome Lite」はGoogle Gearsを搭載しているため、ひょっとしたらGearsのGeolocation API使えるかも!? と思って実験。

Google Gears での位置情報取得

Geolocation API – Gears API – Google Code

サンプルのとおりに、 gears_init.js をおいて、

<script type="text/javascript" src="gears_init.js"></script>
<script type="text/javascript">
var geo = google.gears.factory.create('beta.geolocation');

function updatePosition(position) {
  alert('Current lat/lon is: ' + position.latitude + ',' + position.longitude);
}

function handleError(positionError) {
  alert('Attempt to get location failed: ' + positionError.message);
}

geo.getCurrentPosition(updatePosition, handleError);
</script>

と書くだけで、位置情報取得できた!簡単。

Google Maps API V3 を使って地図表示

それに、Google Maps API V3 を組み合わせる。

The Google Maps API V3 – Google Maps JavaScript API V3 – Google Code

Google Maps API V3 ってのは、今までのMaps APIで効率が悪かったり、スマートフォンに最適化されてなかった部分を改善するため、一から書き直したAPIで、今までのMaps APIと書き方が異なる。

んでも、やってることは同じだし、まだ V3のほうができることが少ないので理解は早いと思います。

サンプルとしては、

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
  function initialize() {
    var myLatlng = new google.maps.LatLng(-34.397, 150.644);
    var myOptions = {
      zoom: 8,
      center: myLatlng,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    }
    var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
  }
</script>
</head>
<body style="margin:0px; padding:0px;" onload="initialize()">
  <div id="map_canvas" style="width:100%; height:100%"></div>
</body>

これで、全画面の地図表示。

  • APIキーがいらない → Open Socialなアプリ作る場合に問題だった、ドメインごとにAPIキー発行する手間がなくなる。
  • スマートフォンに対応 → iPhoneとAndroidのUIにあわせてコントローラーを配置してくれるし、地図の描画までが早い!

だから、iPhoneのネイティブアプリ作ってて地図周りだけ、UIWebView使ってるっていう人も恩恵あるんじゃないかな?

実際に現在位置をTwitterにPOSTするJavaScriptを作ってみた

Google Maps API V3 with Gears Geolocation API @inagaki.co.uk

実際にAndroidで試してみてください。

中身はコチラ。

<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Google Maps API V3 with Gears Geolocation API</title>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="gears_init.js"></script>
<script type="text/javascript">

/*
 * GeoPo Encode in JavaScript
 * @author : Shintaro Inagaki
 * @param location (Object)
 * @return geopo (String)
 */
function geopoEncode(location){
    // 64characters (number + big and small letter + hyphen + underscore)
    var chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";

    var geopo = new String();
    var lat = parseFloat(location.lat); // Parse as float
    var lng = parseFloat(location.lng); // Parse as float
    var scale = parseInt(location.scale); // Parse as int
    
    // Change a degree measure to a decimal number
    lat = (lat + 90) / 180 * Math.pow(8, 10);
    lng = (lng + 180) / 360 * Math.pow(8, 10);
    
    // Compute a GeoPo code from head and concatenate
    for(var i = 0; i < scale; i++) {
        geopo = geopo + chars.substr(Math.floor(lat / Math.pow(8, 9 - i) % 8) + Math.floor(lng / Math.pow(8, 9 - i) % 8) * 8, 1);
    }
    return geopo;
}

var geo = google.gears.factory.create('beta.geolocation');

function displayMap(position) {
    var geocoder = new google.maps.Geocoder();
    var latLng = new google.maps.LatLng(position.latitude,position.longitude);
    var options = {
        zoom: 15,
        center: latLng,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    }
    var map = new google.maps.Map(document.getElementById("map_canvas"), options);

    var geocodeAddress;

    var infowindow = new google.maps.InfoWindow();

    var marker = new google.maps.Marker({
        position: latLng, 
        map: map,
        title:"You are here."
    });

    // geopo
    var location = new Object();
    location.lat = position.latitude;
    location.lng = position.longitude;
    location.scale = 7;

    var geopo = geopoEncode(location);

    if (geocoder) {
        geocoder.geocode({'latLng': latLng}, function(results, status) {
            if (status == google.maps.GeocoderStatus.OK) {
                for(i=1;i<results.length;i++){
                    if (results[i].types.length >= 2 && results[i].types[1] == "political") {
                        if(results[i].formatted_address.indexOf("日本") != -1){
                            geocodeAddress = results[i].formatted_address.substring(2);
                        }else{
                            geocodeAddress = results[i].formatted_address;
                        }
                        break;
                    }
                }
                if(geocodeAddress){
                    infowindow.set_content('<strong>現在地:</strong><br /><span style="font-size:80%">'+geocodeAddress+'</span><br /><strong>GeoPo:</strong><br /><span style="font-size:80%">http://geopo.at/'+geopo+'</span><hr /><a href="http://twitter.com/home?status='+encodeURIComponent(' L:'+geocodeAddress+' http://geopo.at/'+geopo)+'" target="twitter">TwitterにPOSTする</a>');
                    infowindow.open(map, marker);
                } else{
                    alert("現在地が取得できませんでした><");
                }
            } else {
                alert("Geocoder failed due to: " + status);
            }
        });
    }
    
    google.maps.event.addListener(marker, 'click', function() {
        infowindow.open(map,marker);
    });
}

function handleError(positionError) {
    alert('Attempt to get location failed: ' + positionError.message);
}

geo.getCurrentPosition(displayMap, handleError);

</script>
</head>
<body style="margin:0px; padding:0px;">
  <div id="map_canvas" style="width: 100%; height: 100%;"></div>
</body>
</html>

 

こんな風に現在位置が取れるよ

1.さきほどのURLにアクセスする

device01

すると、Gearsで「現在地情報にアクセス」しますかというメッセージがでるので、OKを押してください。ドメインごとの許可ですかね?

2. 現在地の地図を表示

device02

Gearsからの位置情報をMaps API V3で地図表示。ちゃんと、拡大縮小コントローラーもAndroidなUIになってる。

そして、Maps API V3の逆ジオコーディングを使って、現在住所を取ってくる。

ついでに、GeoPoのエンコードもしてるよ!

一番下のリンクがTwitterにポストするためのリンク。

3.TwitterにPOST内容を渡す

device03

http://twitter.com/home/?status=[ポスト内容] で渡すことができるから、単純に URLエンコードしたものを渡してるだけ。

ただ、このようなTwitterのパラメータ渡しは、Twitterの表示モードがモバイルになってると無効になるみたいなので、あらかじめスタンダードに変更しておいてください。

ブラウザでのGeolocation APIについて

実は、Google Gearsに対応していれば、ChromeとかFirefox(要アドオン)でも上記のURLで現在地の位置情報が取得できる。

正確にいうと、IPから推測した位置情報なので正しくはないかもしれないけど、JavaScriptだけの実装で簡易的に位置情報が取れるのは素敵ですね。

将来的には、W3CのGeolocation APIが載って、開発者側はハードウェア、ソフトウェアの差異を何も考えなくて位置情報を使ったコンテンツに注力できるっていう世界になってくれると幸せです。

Windows 7 では、OSレベルにGeolocation APIがあるそうなので、そちらも楽しみ… その前にIEをなんとかしてk(ry


タグ: , , , , ,

コメント / トラックバック 5 件

  1. Chrome Lite

    Androidに搭載されているChrome Liteを使えば Geolocation APIが利用できるとの記載がありました。Androidな方は試していただいてよろしいでしょうか?

  2. […] Google Maps API V3 と Gears Geolocation API 使って Android のブラウザで現在位置情報を取得する | クレコ AndroidでGPSデータ取得 […]

  3. […] Google Maps API V3 と Gears Geolocation API 使って Android のブラウザで現在位置情報を取得する | クレコ では、Androidのブラウザで現在位置情報取得してTwitterにポストするというJavaScriptを作りまし […]

  4. inagaki より:

    というわけで、改善。

    逆ジオコーディングの部分で、typesがpoliticalの場合のみ採用(道路名と郵便番号は除外)
    あとはcountryから組み立てたほうがいいのかもしれないけど、面倒なので(海外と日本では住所の組み立てが違うので。)formatted_addressを採用し、日本の場合「日本」ってのが気持ち悪いのでとってやる処理

    以上。

  5. inagaki より:

    あ、逆ジオコーディングのところで、道路名や郵便番号拾ってこないように処理する中で「日本」ってキーを決めうちしてるから、海外でつかえねーなw
    responseを見て typesキーで判断したほうがいいと思います。うん。