2009年3月10日

モバイル

携帯電話と位置情報 : 現在地の測位 (2)

  • このエントリーをはてなブックマークに追加

こんにちは、広告本部ながやです。

前回の記事では仕様面を記述しましたので、
今回は実際のコードを公開してみようと思います。

例をシンプルにするために、基地局の緯度/経度取得のみ対応とします。
GPS(衛星利用測位システム)機能搭載端末やWILLCOM端末は実装から省きますが、少しの拡張で対応できると思います。
端末をお持ちの人はぜひ挑戦してみてください。

サンプルコード

  • index.php ※1,※2,※3,※4,※5
    <?php
    function array_val(&$data, $key, $default = null) { 
      if (!is_array($data)) {
        return $default;
      }
      return isset($data[$key])? $data[$key]: $default;
    }
    
    function buildGeoLink($target_url, $carrier) {
      // DoCoMo Cell
      if ($carrier == "i_cell") {
        if (strlen($target_url) > 256) {
          return false;
        }
        $url = 'http://w1m.docomo.ne.jp/cp/iarea';
        $ret = array(
            'url'   => $url,
            'attr'  => '',  
            'params'=> array(  
              'ecode'   => 'OPENAREACODE', 
              'msn'     => 'OPENAREAKEY',  
              'posinfo' => '1', 
              'nl'    => $target_url,
              'arg1'    =>'guid=ON'
              ),      
            );      
        // SoftBank Cell
      } else if ($carrier == "s_cell") {
        $url = sprintf('location:cell?url=%s', $target_url);
        $ret = array(
            'url'  => $url,
            'attr'   => '',  
            'params' => '',  
            );      
        // au Cell 
      } else if ($carrier == "a_cell") {
        if (strlen($target_url) > 128) {
          return false;
        }
        $url = sprintf('device:location?url=%s', urlencode($target_url));
        $ret = array(
            'url'    => $url,
            'attr'   => '',  
            'params' => '',  
            );      
      } else {
        return false;
      }
    
      $ret['href_url'] = $ret['url'];
      if(is_array($ret['params'])) {
        $params = http_build_query($ret['params']);
        $separator = strpos($ret['url'], '?') === false ? '?' : '&';
        $ret['href_url'] = $ret['url'].$separator.$params;
      }
      return $ret;
    }
    
    function parseGeocode() {
      $ret = array('lat' => '', 'lon' => '', 'geo' => ''); 
    
      $ret['lat'] = array_val($_REQUEST, 'lat', $ret['lat']);
      $ret['lon'] = array_val($_REQUEST, 'lon', $ret['lon']);
      $ret['lat'] = array_val($_REQUEST, 'LAT', $ret['lat']);
      $ret['lon'] = array_val($_REQUEST, 'LON', $ret['lon']);
    
      $pos = array_val($_REQUEST, 'pos', '');
      $pos = array_val($_REQUEST, 'POS', $pos);
      if (preg_match( "/^N(.+)E(.+)$/", $pos, $match)) {
        $ret['lat'] = $match[1];
        $ret['lon'] = $match[2];
      }
    
      $geo    = array_val($_REQUEST, 'geo', '');
      $geo    = array_val($_REQUEST, 'GEO', $geo);
      $datum  = array_val($_REQUEST, 'datum', '');
      if ($geo == 'wgs84' || $geo == 'WGS84' || $datum == '1') {
        $ret['geo'] = 'wgs';
      }
      return $ret;
    }
    ?>
    <?php
    // FIXME: 測位後の遷移先(このサンプルコードのURL)を記述  
    $target_url = '';
    $geocode = parseGeocode(); 
    ?>
    <html>
    <head>
    <title>geo test</title>
    </head>
    <body>
    lat = <?php echo htmlspecialchars($geocode['lat']); ?><br/>
    lon = <?php echo htmlspecialchars($geocode['lon']); ?><br/>
    geo = <?php echo htmlspecialchars($geocode['geo']); ?><br/>
    <?php $cell = buildGeoLink($target_url,'i_cell'); ?>
    <a href="<?php echo htmlspecialchars($cell['href_url'], ENT_QUOTES); ?>" <?php echo $cell["attr"]; ?>>DoCoMo Cell</a><br/>
    <?php $cell = buildGeoLink($target_url,'s_cell'); ?>
    <a href="<?php echo htmlspecialchars($cell['href_url'], ENT_QUOTES); ?>" <?php echo $cell["attr"]; ?>>SoftBank Cell</a><br/>
    <?php $cell = buildGeoLink($target_url,'a_cell'); ?>
    <a href="<?php echo htmlspecialchars($cell['href_url'], ENT_QUOTES); ?>" <?php echo $cell["attr"]; ?>>au Cell</a><br/>
    </body>
    </html>
    
    

※1 実際には、MVCモデルなどを考慮してきちんと関数化・クラス化しましょう。
※2 キャリアの判定ロジックは実装していません。
※3 $target_urlに含まれるクエリパラメータの強制削除は実装していません。
※4 Yahoo!社内では、独自のモバイル環境向けのプラットフォームを利用するため、個々のエンジニアがこのような実装を行う必要はありません。
※5 本プログラムはphpのバージョン5以上で動作します。

ローカルサーチAPIをたたこう

このサンプルを実際に動かすことができた人は、現在地の緯度(lat)と経度(lon)を取得できているでしょう。
あとは、お好きなWebAPIを使って情報を表示したり、お気に入りのサイトにリンクするだけです。
完成すれば、外出先でも使えるすてきなマッシュアップサイトになるでしょう。

まず最初にたたくWebAPIとして、私は弊社のローカルサーチAPIをオススメします。
このWebAPIは、取得できた緯度/経度周辺に関する以下の情報を簡単に取得できます。

  • Address - 住所
  • Zipcode - 郵便番号
  • Landmark - 施設
  • Station - 駅
  • Courpon - Yahoo!クーポン

WebAPI利用に必要なアプリケーションIDの登録は、Yahoo!デベロッパーネットワークからできます。
また、複数のWebAPIとの通信を考えている人は、APIとの通信効率をよくする実装例(1) curl_multiも参考になるでしょう。

クエリパラメータ

前回の記事で、URLを各キャリアで共通にする場合は、クエリーパラメータは利用不可。
しかし、Yahoo!マチモバで制限を回避する実装をしていると言いました。
このアイデアを軽く紹介したいと思います。

アイデアは単純で、クエリパラメータを文字列として、URL上に展開してしまえというものです。
パラメータを独自にエンコード/デコードの処理などを追加で実装する必要があるのが難点ですが、
検索ワードなどのパラメータを引き継ぐことが可能になるので検討しても良いでしょう。

  • 展開前のURL例
    http://〜URL〜/local/?p=ラーメン
  • 展開後のURL例
    http://〜URL〜/local/cD3jg6njg7zjg6Hjg7M,

クエリパラメータのエンコード/デコードは、PHPマニュアルでも紹介されている以下のようなアルゴリズムで大丈夫だと思います。

<?php
function base64_url_encode($input) {
    return strtr(base64_encode($input), '+/=', '-_,');
}

function base64_url_decode($input) {
    return base64_decode(strtr($input, '-_,', '+/='));
}
?>

このコードを動かし、WebAPIをたたかれた皆様は、それぞれにとてもすてきなマッシュアップサービス(Location-Aware Computing)を実現されたと思います。
また、識者なら次の快適性を求めて、Context-Aware Computingなどと呼ばれる、人の行動(前後関係を含む)に着目したサービスを考えているでしょう。
課題も多い分野ですが、執事ブームもあり、個人的に非常に興味を持っている分野であります。

こちらについても機会と面白いネタがあれば、何かを投稿したいと思います。

以上です。ありがとうございました。


Yahoo!デベロッパーネットワークはこちら
アプリケーションIDの登録はこちら

※現在、Yahoo!マチモバはYahoo!ロコに統合されました。

Yahoo! JAPANでは情報技術を駆使して人々や社会の課題を一緒に解決していける方を募集しています。詳しくは採用情報をご覧ください。

  • このエントリーをはてなブックマークに追加