Androidから,Google Maps Engine APIを使う例です.なお,マップの中身を取得することはできなかったため,実用上はあまり意味がないです orz

AndroidからMaps Engine APIを使う

マイマップはGoogle Maps Engine Liteに統合されるらしいという話があります.

それで,このMaps Engine LiteをAndroidから扱えないか,参照できないかということで,調べてみました.

なお最後に書いていますが,結局マップの中身を取得することはできなかったため,このページの内容は実用上はあまり意味がないです.orz

APIの利用登録

APIを使う時の認証の話は, Installed Application Authentication - Google Maps Engine API に書かれています.基本的にはこれに従うことにします.

ただAndroidなので,端末に設定されているアカウントをAccountManager経由で使うということで,色々と違ってきます.

APIの利用登録ですが, Google API Console から,利用するアプリの署名とパッケージ名を登録して下さい.

Mapsなど他のAPIを使う場合と同じなので特に説明しませんが,ポイントは
1021-1.jpg

  • Servicesで「Google Maps Engine API」をONにする
  • API Accessで登録しますが,必ず「Authorized API Access(OAuth 2.0)」で登録する.「Installed application」-「Android」です.Google Maps APIなどと同じ調子で「Simple API Access」に登録すると,駄目です

ぐらいだと思います.

ライブラリの準備

素直に Google APIs Client Library for Java を使うことにします.

試した時点の最新版,

  • google-api-java-client-1.17.0-rc.zip

を使いました.

ダウンロードしたzipを展開すると色々jarがありますが,自分のアプリのプロジェクトに以下のファイルを追加しました.

libsに入れて,ビルドパスに追加したファイル

commons-codec-1.3.jar
commons-logging-1.1.1.jar
google-api-client-1.17.0-rc.jar
google-api-client-android-1.17.0-rc.jar
google-http-client-1.17.0-rc.jar
google-http-client-android-1.17.0-rc.jar
google-http-client-gson-1.17.0-rc.jar
google-oauth-client-1.17.0-rc.jar
gson-2.1.jar
httpclient-4.0.1.jar
httpcore-4.0.1.jar
jsr305-1.3.9.jar
なお,commons-codec-1.3.jarはgoogle-api-java-clientに同梱されていないので,
http://repo.maven.apache.org/maven2/commons-codec/commons-codec/1.3/commons-codec-1.3.jar
からダウンロードしました.

このあたりの依存関係の情報は,google-api-java-clientのzipを展開した中身の

dependencies/google-api-client-android-dependencies.html
に書かれています.

Google Play Services APIの追加も必要です.Google Maps Android API v2を使うときと同じですが,google-play-services_lib のライブラリプロジェクトを参照する設定にして下さい.

AndroidのAccountManagerを認証に使う場合の参考資料は,

また,Maps Engine API固有の処理についてですが,Fusion Tablesなど他のAPIは,インターフェイスのラッパーライブラリが用意されていて,

からダウンロードできます.しかしMaps Engineのものは無かったので,自前で実装します.

ソースコード

mapsパスを呼び出して,mapId指定でmapのメタデータを取得する例です.logcatに出力します.

tasks-android-sampleのソースを元に,改造したものです.

MainAct.java

package com.kamoland.gapitest.tasks;

import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpTransport;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;

public class MainAct extends Activity {

	private static final String PREF_ACCOUNT_NAME = "accountName";

	private static final int REQUEST_GOOGLE_PLAY_SERVICES = 0;
	private static final int REQUEST_AUTHORIZATION = 1;
	private static final int REQUEST_ACCOUNT_PICKER = 2;

	private GoogleAccountCredential credential;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		credential = GoogleAccountCredential
				.usingOAuth2(this,Collections.singleton(
					"https://www.googleapis.com/auth/mapsengine.readonly"));
//					"https://www.googleapis.com/auth/mapsengine")); // read and write

		SharedPreferences settings = getPreferences(Context.MODE_PRIVATE);
		credential.setSelectedAccountName(settings.getString(PREF_ACCOUNT_NAME, null));
	}

	@Override
	protected void onResume() {
		super.onResume();

		if (checkGooglePlayServicesAvailable()) {
			haveGooglePlayServices();
		}
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);

		if (requestCode == REQUEST_GOOGLE_PLAY_SERVICES) {
			if (resultCode == Activity.RESULT_OK) {
				haveGooglePlayServices();
			} else {
				checkGooglePlayServicesAvailable();
			}

		} else if (requestCode == REQUEST_AUTHORIZATION) {
			if (resultCode == Activity.RESULT_OK) {
			} else {
				chooseAccount();
			}
		} else if (requestCode == REQUEST_ACCOUNT_PICKER) {
			if (resultCode == Activity.RESULT_OK && data != null && data.getExtras() != null) {
				String accountName = data.getExtras().getString(AccountManager.KEY_ACCOUNT_NAME);
				if (accountName != null) {
					credential.setSelectedAccountName(accountName);
					SharedPreferences.Editor editor = getPreferences(Context.MODE_PRIVATE).edit();
					editor.putString(PREF_ACCOUNT_NAME, accountName);
					editor.commit();
				}
			}
		}
	}

	private boolean checkGooglePlayServicesAvailable() {
		final int connectionStatusCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
		if (GooglePlayServicesUtil.isUserRecoverableError(connectionStatusCode)) {
			runOnUiThread(new Runnable() {
				@Override
				public void run() {
					GooglePlayServicesUtil.getErrorDialog(connectionStatusCode,
							MainAct.this, REQUEST_GOOGLE_PLAY_SERVICES).show();
				}
			});
			return false;
		}
		return true;
	}

	private void haveGooglePlayServices() {
		if (credential.getSelectedAccountName() == null) {
			chooseAccount();
		} else {
			mapEngineRequest();
		}
	}

	private void chooseAccount() {
		startActivityForResult(credential.newChooseAccountIntent(),
				REQUEST_ACCOUNT_PICKER);
	}

	private void mapEngineRequest() {
		final String mapId = "z8XxNtJBE8Uw.kJn9Fyq397d8";

		(new Thread() {
			@Override
			public void run() {
				try {
					HttpTransport httpTransport = AndroidHttp.newCompatibleTransport();
					HttpRequestFactory factory = httpTransport.createRequestFactory(credential);
					HttpRequest request = factory.buildGetRequest(new GenericUrl(
							"https://www.googleapis.com/mapsengine/v1/maps/" + mapId
							));

					HttpResponse response = request.execute();

					BufferedReader reader = new BufferedReader(
							new InputStreamReader(response.getContent()));

					for (String line = null; (line = reader.readLine()) != null;) {
						log(line);
					}

				} catch (UserRecoverableAuthIOException uraex) {
					startActivityForResult(
							uraex.getIntent(),
							MainAct.REQUEST_AUTHORIZATION);

				} catch (IOException ex) {
					throw new RuntimeException(ex);
				}
			}
		}).start();
	}

	private static void log(String mes) {
		Log.d("**GApiTest", mes);
	}
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="com.kamoland.gapitest.tasks"
	android:versionCode="1"
	android:versionName="1.0" >

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

	<uses-sdk
		android:minSdkVersion="5" android:targetSdkVersion="9" />

	<application android:label="GAPI Test">
		<activity
			android:name=".MainAct" android:launchMode="singleTop" >
			<intent-filter>
				<action android:name="android.intent.action.MAIN" />
				<category android:name="android.intent.category.LAUNCHER" />
			</intent-filter>
		</activity>
	</application>
</manifest>
AccountManagerを使っているので,minSdkVersionは5にしています.

これだけの実装で,使用するアカウントの選択ダイアログや,Maps Engineの権限確認のダイアログが表示されて,動きます.

実行結果

ログに以下の内容が出力されれば成功です.Android 2.3.3以上で確認しました.
10-21 09:44:00.322: D/**GApiTest(9834): {
10-21 09:44:00.322: D/**GApiTest(9834):  "id": "z8XxNtJBE8Uw.kJn9Fyq397d8",
10-21 09:44:00.322: D/**GApiTest(9834):  "name": "大阪城の地図",
10-21 09:44:00.322: D/**GApiTest(9834):  "description": "大阪城です",
10-21 09:44:00.322: D/**GApiTest(9834):  "versions": []
10-21 09:44:00.322: D/**GApiTest(9834): }
mapIdは,Web版のMaps Engine Liteで「共有」を押したときに出力されるURL
https://mapsengine.google.com/map/edit?mid=z8XxNtJBE8Uw.kJn9Fyq397d8
のmidが対応しています.

このマップはテスト用に,誰でも閲覧できるという設定にしています.

しかしマップの中身は...

で,マップのメタデータは取得できたのですが,それだけでは嬉しくないのデス...

Maps Engine APIの仕様によると,マップの中身を取得するには, tableIdを指定してfeaturesを取得する 必要がありそうですが,Liteの制限なのかよくわかりませんが,tableIdを知るすべがなさそうです.

...手詰まりになってしまいました.

ちなみに,中身のKMLを,Maps Engine以外の方法で直接取得する方法を試された方もいるようですが, 現在はMaps Engine自体がKML出力機能を持っているため,中止されたようです.

(;´Д`)

kamolandをフォローしましょう


© 2021 KMIソフトウェア