[[勉強会]]
* Androidでマップアプリ [#z7eee74b]

* Androidでマップアプリ [#pb339aad]

AndroidでGoogle Maps を使用したアプリケーションを作成してみましょう。
Androidでは、位置情報を元にしたサービスを、位置情報サービス(LBS:Location-Based Services)と呼んでいます。

** 作成するアプリケーション [#g2386676]
** 作成するアプリケーション [#f2bb411b]

Android上に地図を表示して、タップした位置にアイコンを描画するアプリです。

以下のページを参考にしながら、作成してみましょう。

http://developer.android.com/guide/tutorials/views/hello-mapview.html

** 作成手順の概略 [#m0935527]
** 作成手順の概略 [#x6a583fd]

アプリケーションを作成する手順の概略です。

- Google MapsのAPIキーを取得します
-- アプリに署名する証明書が必要です
- 新規のプロジェクトを作成します
-- SDK1.5ではライブラリのAdd-onが必要です
- マニフェストファイルを設定します
-- ライブラリやパーミッションの設定が必要です
- MapViewで地図を表示します
-- 取得したAPIキーを使用します
- MapActivityで地図の表示を制御します
-- ズームコントローラで地図の拡大縮小できます
- タップされた位置にアイコンを描画します
-- Overlayクラスで地図上に描画できます

** Google MapsのAPIキー取得 [#wa62d81a]
** Google MapsのAPIキー取得 [#bea5b6c4]

Google MapsはGoogleが提供するWeb上のサービスです。Androidでは、このサービスにアクセスするための専用のライブラリが提供されています。
このライブラリは、Googleが提供しているものであり、オープンソースのAndroidの一部ではありません。このため、このライブラリを使用するには、Googleに開発者としての登録を行う必要があります。登録を行うと、APIキーという文字列が発行されます。この文字列をアプリケーション内に組み込みむことで、サービスにアクセスすることが可能になります。

APIキーの取得には、アプリケーションの開発時に使用する証明書のハッシュが必要になります。証明書とは、開発者の名前や所属に、秘密鍵で署名したものです。

[[Ceart.png]]
#ref(Ceart.png)

Androidのアプリケーションのパッケージには、開発者自身の証明書による署名が必要になります。デバッグには、開発ツールが自動的に作成した証明書が使用されてるのであまり意識することがありませんが、エミュレータや携帯端末の実機に転送する前に常に署名がおこなわれています。

証明書のMD5のハッシュは、SunのJDKに含まれているkeytoolという証明書操作用のツールを使用します。まずは、keytoolがインストールされているか、以下のコマンドを実行して確認しましょう。

> keytool -help

もし、コマンドが見つからない場合には、JDKをインストールしたディレクトリのbinディレクトリを確認してみましょう。以下の、%JAVA_HOME%をJDKをインストールしたディレクトリに置き換えて実行してみましょう。うまく実行できれば、PATH環境変数に、%JAVA_HOME%\binを追加しておきます。

> "%JAVA_HOME%\bin\keytool" -help
...

つぎに、使用している証明書のMD5のハッシュを取ります。デバッグ時に使用される証明書は、使用しているOSに応じて、以下の場所に格納されています。

- OS X	~/.android/debug.keystore
- Linux	~/.android/debug.keystore
- SDK1.1 Windows
-- Windows Vista	C:\Users\%USERNAME%\AppData\Local\Android\debug.keystore
-- Windows XP	C:\Documents and Settings\%USERNAME%\Local Settings\Application Data\Android\debug.keystore
- SDK1.5 Windows
-- Windows Vista	C:\Users\%USERNAME%\.android\debug.keystore
-- Windows XP	C:\Documents and Settings\%USERNAME%\.android\debug.keystore

keytoolの-listコマンドで証明書の一覧を表示します。例えば、SDK1.5のVistaの場合には、以下のコマンドを実行します。パスワードは、入力しないで単純にリターンを押すだけで大丈夫です。(入れたい人は、"android"と入れてください)

 > keytool -list -keystore C:\Users\%USERNAME%\.android\debug.keystore
 キーストアのパスワードを入力してください: android
 
 キーストアのタイプ: JKS
 キーストアのプロバイダ: SUN
 
 キーストアには 1 エントリが含まれます。
 
 androiddebugkey, 2008/10/14, PrivateKeyEntry,
 証明書のフィンガープリント (MD5): 4B:28:72:FB:D3:2C:86:D5:A5:F8:B4:BA:09:C3:90:29

ここで表示されたフィンガープリントを、以下のページで入力します。

- http://code.google.com/intl/ja/android/maps-api-signup.html

[[AndroidMapsAPI.jpg]]
#ref(AndroidMapsAPI.jpg)

生成したフィンガープリントは、後で使用するので、控えておきます。

[[AndroidMapsAPIKey.jpg]]
#ref(AndroidMapsAPIKey.jpg)

アプリケーションの署名は、端末のインストール時と、マーケットへのアップロード時に確認されます。

[[Sign.png]]
#ref(Sign.png)

* プロジェクトの作成 [#c0368e3f]
* プロジェクトの作成 [#yed9820e]

新規のプロジェクトを作成します。

- File New => New => Android Project 
-- 今回はSDK1.1を選択します。

[[NewAndroidProject.jpg]]
#ref(NewAndroidProject.jpg)

* アイコンを変更します。 [#oa2fd39f]
* アイコンを変更します。 [#uc7da050]

アイコンは、ユーザーが認識可能なアプリケーションの識別子です。まずは、アイコンを書き換えてみましょう。
res/drawable/icon.pngをペイントなどで編集します。

* マニフェストファイルの設定 [#wab8c921]
* マニフェストファイルの設定 [#i2b1bcd4]

地図を使用するには、マニフェストのapplication内にライブラリを使用するを宣言します。EclipseのGUIでは、AndroidManifestのApplicationタブの下の「Application Nodes」で設定します。

- <uses-library android:name="com.google.android.maps" />


[[AndroidManifest.jpg]]
#ref(AndroidManifest.jpg)

また、インターネットに接続するために、同じくマニフェスト内で以下のパーミッションの使用を宣言します。EclipseのGUIでは、AndroidManifestのPermissionタブで設定します。

- <uses-permission android:name="android.permission.INTERNET" />

[[AndroidManifestPermission.jpg]]
#ref(AndroidManifestPermission.jpg)


* レイアウトの作成 [#d38ff690]
* レイアウトの作成 [#xbd608a8]

- レイアウトリソースファイル(res/layout/main.xml)を以下のように書き換えます。

 <?xml version="1.0" encoding="utf-8"?>
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/mainlayout"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >
 
    <com.google.android.maps.MapView
        android:id="@+id/mapview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:clickable="true"
        android:apiKey="取得したAPI Key"
    />
 
    <LinearLayout
        android:id="@+id/zoomview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@id/mapview"
        android:layout_centerHorizontal="true"
    />
 </RelativeLayout>

ここでは、相対レイアウトの中に、MapViewと、リニアレイアウトを作成しています。リニアレイアウトは、後でマップのズームコントローラを配置するために使用します。

* MapViewクラスを作成する [#o561648f]
* MapViewクラスを作成する [#qf1bcee7]

- MapViewクラスを表示して、MapActivityを継承させます。これによって、MapViewの制御をこのクラスが行えるようになります。(Shift-Ctrl-Oでcom.google.android.maps.MapActivityをインポートします。)

 public class HelloMapView extends MapActivity

- Add unimplement methodを使用して、isRouteDisplayedメソッドをオーバーライドします。

 @Override
 protected boolean isRouteDisplayed() {
     // TODO Auto-generated method stub
     return false;
 }

ここで、アプリケーションを実行して、地図が表示されることを確認しましょう。もし、地図が表示されてない場合には、以下の項目を確認してみましょう。

- SDKにあらかじめインストールされているMapアプリケーションで地図が表示されるか確認する。
-- 表示されてなけば、ネットワークの設定などOSの設定を確認する。Proxyが設定されていると、表示できないので注意する。
- MapのAPIキーが正しく入力されているか確認する。APIキーの取得は何度でも行えるので、再度取得し直してみる。
- 使用している証明書が正しいか確認する。Windowsでは、SDK1.1からSDK1.5で証明書の位置が変更になっているので、注意する


* ズームコントローラを配置する。 [#qdfcfc1b]
* ズームコントローラを配置する。 [#be9273ca]

コンストラクタを以下のように変更します。

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        LinearLayout linearLayout;
        MapView mapView;
        ZoomControls mZoom;
        
        linearLayout = (LinearLayout) findViewById(R.id.zoomview);
        mapView = (MapView) findViewById(R.id.mapview);
        mZoom = (ZoomControls) mapView.getZoomControls();
        
        linearLayout.addView(mZoom);
    }
    
実行して、地図を表示してみましょう。タップするとズームコントローラが表示されます。

* 初期値を設定する [#y1baf430]
* 初期値を設定する [#w65f3036]

以下のフィールドを定義します。

    // ログ出力用のタグ
    static final String TAG = "HelloMapActivity";
 
    // 地図の初期値
    static final int INITIAL_ZOOM_LEVE = 16;
    static final int INITIAL_LATITUDE = 35156807;
    static final int INITIAL_LONGITUDE = 136925412;

コンストラクタに最後に、以下の行を追加します。

        // 位置とズームレベルの初期状態を設定する
        MapController controller = mapView.getController();
        GeoPoint point = 
            new GeoPoint(INITIAL_LATITUDE, INITIAL_LONGITUDE);
        controller.setCenter(point);
        controller.setZoom(INITIAL_ZOOM_LEVE);

実行してみましょう。

* タップされた位置にアイコンを描画する [#zb424092]
* タップされた位置にアイコンを描画する [#nb14aa61]

Overlayクラスを使用して、地図上に描画できます。
コンストラクタの最後に以下の呼び出しを追加します。

        // イメージを地図上に表示する
        setOverlay(point);

以下のプライベートメソッドを定義します。

 
    private void setOverlay(GeoPoint point) {
 
        // Overlayクラスを生成する
        Bitmap icon = BitmapFactory.decodeResource(getResources(),
                                                   R.drawable.icon);
        IconOverlay overlay = new IconOverlay(icon, point);
 
        // 生成したOverlayクラスを追加する
        MapView map_view = (MapView) findViewById(R.id.mapview);
        List<Overlay> overlays = map_view.getOverlays();
        overlays.add(overlay);
    }
 
    // 地図上に表示されるオーバーレイ
    private class IconOverlay extends Overlay {
 
        // 描画するアイコン
        Bitmap mIcon;
        int mOffsetX;
        int mOffsetY;
 
        // アイコンを表示する位置
        GeoPoint mPoint;
 
       IconOverlay(Bitmap icon, GeoPoint initial) {
            // アイコンと、アイコンの中心のオフセット
            mIcon = icon;
            mOffsetX = 0 - icon.getWidth() / 2;
            mOffsetY = 0 - icon.getHeight() / 2;
            mPoint = initial;
        }
 
        // 地図のタップ時に呼び出される
        @Override
        public boolean onTap(GeoPoint point, MapView mapView) {
            // タップされた位置を記録する
            mPoint = point;
            Log.i(TAG, "Point = " + point.getLatitudeE6() + " , " + point.getLongitudeE6());
            return super.onTap(point, mapView);
        }
 
        // 地図の描画時に、shadow=true, shadow=falseと2回呼び出される
        @Override
        public void draw(Canvas canvas, MapView mapView,
                         boolean shadow) {
            super.draw(canvas, mapView, shadow);
            if (!shadow) {
                // 地図上の場所と、描画用のCanvasの座標の変換
                Projection projection = mapView.getProjection();
                Point point = new Point();
                projection.toPixels(mPoint, point);
                point.offset(mOffsetX, mOffsetY);
                // アイコンを描画
                canvas.drawBitmap(mIcon, point.x, point.y, null);
            }
        }
    };

実行してみましょう。

----
[[勉強会]]