デベロッパー

デベロッパー

AndroidでのGoogle APIを使った地図表示

Chunyen Liu
2008年8月22日 / 10:00
 
 

はじめに

 地図表示機能は、今日のモバイル端末にとって必須の機能となりました。新技術の発展により、モバイル端末(とりわけ携帯電話)の能力は、複雑な数値計算処理を単体で行ったり、サーバーとの大容量通信を行ったりするのに十分なレベルに達しています。

 地図表示機能と言えばかつてはGPS機器が主役でしたが、今ではほぼ完全なGPS機能を提供するモバイル端末が非常に増えてきています。GoogleのAndroidでは、Googleのよく知られた地図表示ツールに直接アクセスできます。本稿では、この地図機能を利用するための主なプログラミングAPIを見ていきます。

地図表示用の各種Google API

 まず、必要な開発ツール、プラグイン、サンプルコードはすべてGoogle AndroidのWebサイトにあります。このWebサイトには、利用にあたってのわかりやすい説明も掲載されています。まだご覧になっていなければ、ご一読されることをお勧めします。

 地図表示APIの大半は、com.google.android.mapsパッケージ内にあります。ソフトウェアに地図ツールを組み込むには、MapActivityとMapViewの最低限2つのAPIが必要です。MapActivityは、MapViewの背後でアクティビティのライフサイクルとサービスを管理します。MapViewは、Androidで地図を表示するビューです。これらのAPIのほかに、地図の移動と拡大縮小を行うMapControllerもあります。MyLocationOverlayとOverlayは、地図上にユーザーの設定した情報やオブジェクトを描画するのに使用します。

 地図表示を語るうえで、GPSの存在を忘れるわけにはいきません。GPSは今やモバイル端末に欠かせない機能の1つとなっているからです。そこでGPSサポート用には、android.locationパッケージが用意されています。LocationManagerは最も重要なAPIであり、システムロケーションサービスへのアクセスを行います。地図表示とGPSのAPIは、位置情報サービス(LBS)を行うためには欠かせないものです。以降で紹介するサンプルでは、こうしたAPIをできるだけ多く取り上げます。

MapActivityでMapViewを生成する

 MapViewを生成できるのはMapActivityだけです。なぜなら、MapViewはネットワークとファイルシステムにアクセスするバックグラウンドスレッドに依存しており、このバックグラウンドスレッドはMapActivityによって管理されているからです。そのため、まず次のようにMapActivityを継承するクラスを作成する必要があります。

public class TutorialOnMaps extends MapActivity {
   private static MapView mMapView;

   /** Called when the activity is first created. */
   @Override
   public void onCreate(Bundle icicle) {
      super.onCreate(icicle);

      requestWindowFeature(Window.FEATURE_NO_TITLE);
      setContentView(R.layout.main);

      // Get the map view from resource file
      mMapView = (MapView)findViewById(R.id.mv);
      }
   }
 デフォルトのリソースファイルmain.xml内で、透明なパネルを使ったオンスクリーンボタンをいくつか追加していきます。MapViewを正しく作成するために、Android開発者の間でよく知られている方法を使って、MapViewの宣言を行います。図1に、オンスクリーンボタンを追加したエミュレータの初期画面を示します。

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android=
   "http://schemas.android.com/apk/res/android"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   >

<view id="@+id/mv"
   class="com.google.android.maps.MapView"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:layout_weight="1" />

<LinearLayout xmlns:android=
   "http://schemas.android.com/apk/res/android"
   android:orientation="horizontal"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:background="#550000ff"
   android:padding="2px"
   >
<Button id="@+id/sat"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_marginLeft="40px"
   android:text="Satellite" />
(...snipped)
</LinearLayout>

<LinearLayout xmlns:android=
   "http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
   android:layout_width="wrap_content"
   android:layout_height="fill_parent"
   android:background="#550000ff"
   android:padding="2px"
   >
<Button id="@+id/zin"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_marginTop="30px"
   android:text="+" />
(...snipped)
</LinearLayout>

</FrameLayout>
図1 指定位置を中心とした初期マップ
図1 指定位置を中心とした初期マップ

地図の移動、拡大縮小、モードの切り替え

 先ほど述べたとおり、MapControllerは地図の移動と拡大縮小に使用します。各ボタンをクリックリスナーに関連付ける必要があります。以下はその一例です。

// Set up the button for "Pan East"
mPanE = (Button)findViewById(R.id.pane);
mPanE.setOnClickListener(new OnClickListener(){
   // @Override
   public void onClick(View arg0) {
      panEast();
   }
});
 panEast()関数は、画面サイズの4分の1の幅で地図を動かします。その他の移動も同様に行われます。実際には、タッチスクリーンLCDが搭載されている機器の場合は、この地図だけでドラッグ可能です。タッチスクリーンが搭載されていない機器では、画面上のボタンかパッドの矢印キーを使って地図を動かします。panEast()関数のコードは次のとおりです。

public void panEast() {
   Point pt = new Point(
      mMapView.getMapCenter().getLatitudeE6(),
      mMapView.getMapCenter().getLongitudeE6()
         + mMapView.getLongitudeSpan() / 4);
   mMapView.getController().centerMapTo(pt, true);
   }
 拡大縮小は、MapControllerを通じて現在のズーム倍率を増減させることで簡単に行えます。

public void zoomIn() {
   mMapView.getController().zoomTo(mMapView.getZoomLevel() + 1);
   }
 地図の表示を衛星画像または渋滞状況表示に切り替える機能は、MapViewから直接実現できます。結果を図2および図3に示します。

public void toggleSatellite() {
    mMapView.toggleSatellite();
}

public void toggleTraffic() {
    mMapView.toggleTraffic();
}
図2 衛星画像
図2 衛星画像
図3 衛星画像上での渋滞状況表示
図3 衛星画像上での渋滞状況表示

現在のMapViewの情報を取得する

 MapViewが正しく作成されれば、表示中の画面に関するさまざまな情報を取得できるようになります。表示中の地図のステータスを把握するのに役立つ主な関数を次に示します。

  • getMapCenter ― 地図の中心地点の緯度と経度を返す
  • getLatitudeSpangetLongitudeSpan ― 現在表示されている区域のサイズを返す
  • getZoomLevelgetMaxZoomLevel ― 縮尺レベルを返す(レベルは2倍単位で増減)
  • isSatellite ― 現在の地図の表示が衛星画像モードかどうかをチェックする
  • isTraffic ― 渋滞状況を表示しているかどうかをチェックする
  • isShowMyLocation ― 地図上に現在地が表示されているかどうかをチェックする
  • isStreetView ― 現在の地図表示がストリートビューモードかどうかをチェックする

利用者が設定した情報をMapView上に表示する

 基本クラスのOverlayは、地図上に重ねるオーバーレイを表します。オーバーレイは、地図上にカスタムグラフィックオブジェクトを描画して表示するために使用します。実際のコードでは、基本的にdraw()メソッドをオーバーライドして、デフォルト位置に円を描き、キャプションを透明な角丸長方形のボックスで囲むようにします。

 オーバーライドしたdraw()メソッドのコードを次に示します。このコードでは、位置から画面座標への変換(PixelCalculatorを使用)、フォントメトリクスの計算(PaintのmeasureText()を使用)、アンチエイリアス処理したテキストの描画(PaintのsetAntiAlias()を使用)といった処理を行っています。

// This is used draw an overlay on the map
protected class MyOverlay extends Overlay {
   @Override
   public void draw(Canvas canvas, PixelCalculator pc,
                    boolean shadow) {
      super.draw(canvas, pc, shadow);

      if (mDefCaption.length() == 0) {
         return;
      }

      Paint p = new Paint();
      int[] scoords = new int[2];
      int sz = 5;

      // Convert to screen coords
      pc.getPointXY(mDefPoint, scoords);

      // Draw point caption and its bounding rectangle
      p.setTextSize(14);
      p.setAntiAlias(true);
      int sw = (int)(p.measureText(mDefCaption) + 0.5f);
      int sh = 25;
      int sx = scoords[0] - sw / 2 - 5;
      int sy = scoords[1] - sh - sz - 2;
      RectF rec = new RectF(sx, sy, sx + sw + 10, sy + sh);

      p.setStyle(Style.FILL);
      p.setARGB(128, 255, 0, 0);
      canvas.drawRoundRect(rec, 5, 5, p);
      p.setStyle(Style.STROKE);
      p.setARGB(255, 255, 255, 255);
      canvas.drawRoundRect(rec, 5, 5, p);

      canvas.drawText(mDefCaption, sx + 5, sy + sh - 8, p);

      // Draw point body and outer ring
      p.setStyle(Style.FILL);
      p.setARGB(88, 255, 0, 0);
      p.setStrokeWidth(1);
      RectF spot = new RectF(scoords[0] - sz, scoords[1] + sz,
         scoords[0] + sz, scoords[1] - sz);
      canvas.drawOval(spot, p);

      p.setARGB(255, 255, 0, 0);
      p.setStyle(Style.STROKE);
      canvas.drawCircle(scoords[0], scoords[1], sz, p);
   }
}
 作成したオーバーレイは、使用する前にMapViewのオーバーレイコントローラに追加する必要があります。オーバーレイコントローラのセットアップを行うコードを次に示します。

// Set up the overlay controller
mOverlayController = mMapView.createOverlayController();
MyOverlay mo = new MyOverlay();
mOverlayController.add(mo, true);
図4 オーバーレイで地図上にラベルを表示
図4 オーバーレイで地図上にラベルを表示

現在のGPS位置情報と統合する

 android.locationは、ロケーションプロバイダの一覧を検索して、現在位置の定期的な更新を行うための登録ができるAPIを含んだパッケージです。各ロケーションプロバイダは、/data/misc/location/<provider_name>ディレクトリ配下のファイルを随時更新します。したがって、デフォルトのダミーGPSプロバイダ「gps」の情報は、エミュレータ上では/data/misc/location/gps/に置かれます。GPSファイルの形式はプロバイダごとに異なる可能性があります。詳細な情報については、Androidのサイトを参照してください。

 LocationManagerを初めてアクティブにするときは、初期化を行うために多少時間がかかりますので注意してください。その後に続く呼び出しは、すぐに応答があります。次のコードは、プロバイダからのGPS位置情報を取得して、MapViewのコントローラに送る方法を示しています。

private void centerOnGPSPosition() {
   String provider = "gps";
   LocationManager lm =
      (LocationManager)getSystemService(Context.LOCATION_SERVICE);

   // NOTE: When LocationManager is called the first time,
   //       lat / lon is initialized to 0.
   //       Subsequent calls are fine and fast.
   Location loc = lm.getCurrentLocation(provider);

   mDefPoint = new Point((int)(loc.getLatitude()  * 1000000),
                         (int)(loc.getLongitude() * 1000000));
   mDefCaption = "GPS location";

   mMapView.getController().animateTo(mDefPoint);
   mMapView.getController().centerMapTo(mDefPoint, true);
}
図5 オーバーレイで地図上にGPS情報を表示
図5 オーバーレイで地図上にGPS情報を表示

まとめ

 ここまでの解説を読んで、この地図表示ソフトを試してみたい、独自の機能を追加してみたいと思った読者は多いのではないでしょうか。このソフトウェアパッケージは、本稿のダウンロードサンプルに収録されています。サンプルプロジェクトをEclipseにインポートするだけで使えます。

 Androidの地図表示APIは、既に普及しているGoogleのツールに直接アクセスできるため、簡単に使えてしかも強力です。Androidとより高度なAPIが発展していくにつれて、より多くの地図表示機能がAndroidプラットフォームに移植されることになるでしょう。私が思いつくだけでも、次のようなアイデアがあります。皆さんもぜひ挑戦してみてください。

  • Googleマップを頻繁に利用している方なら、地図の拡大縮小と移動をコントロールできるドラッグ可能なツールバーのスタイルをきっと気に入っているかと思います。このユーザーインターフェイスと機能性にならった機能を開発してみたらどうでしょうか。素晴らしい追加機能になると思います。
  • Googleマップと同様の住所検索機能を追加するというのはどうでしょうか。この機能では、検索結果をいかに解析するかが重要なポイントとなります。
  • 通常の場合、GPSは新しい場所に移動すると、初期化のために多少の時間を必要とします。この待機時間がおおむね5秒を超える場合は、ANR(アプリケーション無応答)ダイアログが表示されることがあります。システムの割り込みを生じさせずにソフトウェアをスムーズに動かすには、そのための子スレッドを作成し、その子スレッドがメッセージ処理スキームを介してメインスレッドとやり取りできるようにする必要があります。

参考資料

著者紹介

Chunyen Liu(Chunyen Liu)
世界トップのGPS企業のエンジニアリング部門に在籍。SUN、ACM、IBMの主催するプログラミングコンテストで複数の入賞経験がある。コンピュータサイエンスの上級学位を保有し、The J Makerという個人サイトを運営。このほか、卓球の腕前は競技者レベルであり、アメリカ卓球協会認定の審判員、指導員という一面も持つ。メールの宛先はこちら
【関連記事】
『Android』搭載携帯は T-Moble USA の救世主となるか?
三井ベンチャーズ、iPhone や Android 対応ソフトウェアのコンテストを開催
『iPhone 3G』発売、次世代モバイルにおける Wozniak 氏の視点
【中国】中国移動、Google「Android」搭載ケータイ市場投入は年末か
salesforce.com と Google が提携を拡大

New Topics

Special Ad

ウマいもの情報てんこ盛り「えん食べ」
ウマいもの情報てんこ盛り「えん食べ」 「えん食べ」は、エンジョイして食べる、エンターテイメントとして食べものを楽しむための、ニュース、コラム、レシピ、動画などを提供します。 てんこ盛りをエンジョイするのは こちらから

Hot Topics

IT Job

Interviews / Specials

Popular

Access Ranking

Partner Sites