国土地理院の電子国土の地図を,Androidで表示するサンプルコード(Java).ドラッグによるスクロールとズームに対応しています.

Androidで電子国土を表示(Java)

Android版の注意事項

  • もともと国土地理院の電子国土 は,Andriodでの動作を保証していません
  • Androidへの対応は,カモランド作者が個人的に行っているものであり,国土地理院は無関係です
  • そのため,電子国土のAndriodでの動作については,国土地理院に問い合わせないようにしてください
  • また,今後の国土地理院側の仕様変更には,できるだけ追随するつもりですが,対応しきれずに動作しなくなる可能性があります

以上を了解の上,このページの内容を利用してください.

概要

国土地理院の電子国土の地図を,Androidアプリで無理矢理表示するサンプルコードです.
  • タッチパッドのドラッグによる,地図のスクロール
  • ズーム

に対応しています.(ただしスクロールは,結構もたつきます orz) → なお,この方法をアプリとして実装したものが,地図ロイドです.

jpmapview.png

ソースコード

アクティビティ
import android.app.Activity;
import android.os.Bundle;
import android.view.ViewGroup;
import android.view.Window;
import android.view.ViewGroup.LayoutParams;

public class MainView extends Activity {
	private CyberJpMapView jpMapView;

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

		requestWindowFeature(Window.FEATURE_NO_TITLE);

		jpMapView = new CyberJpMapView(this);

		setContentView(jpMapView, new LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
				ViewGroup.LayoutParams.FILL_PARENT));
	}
}
アクティビティはシンプルで,実際の処理はWebViewを継承したCyberJpMapViewクラスで行っています.

このクラスで開いているURL(MAP_URL)は,電子国土APIのURLではありません.このあたりの事情については, 電子国土をAndroidのブラウザでを参照してください.

電子国土の地図を表示するビュークラス

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.view.MotionEvent;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

/**
 * 電子国土の地図を表示するビュークラス
 */
public class CyberJpMapView extends WebView {
	private static final String MAP_URL = "http://kamoland.com/comp/mapview-android-app.html";
	private static final int SEMAPHORE_TIMEOUT_MILLIS = 5000;
	private static final int SCROLL_UNIT = 2;

	private Activity act;
	private int dx = 0;
	private int dy = 0;

	private JSInterface jsi;

	public CyberJpMapView(Activity act) {
		super(act);
		this.act = act;

		setFocusable(true);
		setFocusableInTouchMode(true);
		clearCache(true);
		setWebViewClient(new WebViewClient());
		addJavascriptInterface(new JSInterface(),"viewer");
		getSettings().setJavaScriptEnabled(true);
		getSettings().setBuiltInZoomControls(true);

		loadUrl(MAP_URL);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if (event.getAction() == MotionEvent.ACTION_MOVE && event.getHistorySize() > 1) {
			int xmove = Math.round(event.getHistoricalX(event.getHistorySize() - 1) - event.getHistoricalX(0));
			int ymove = Math.round(event.getHistoricalY(event.getHistorySize() - 1) - event.getHistoricalY(0));
			if (xmove != 0 || ymove != 0) {
				dx += xmove;
				dy += ymove;
				if (dx > SCROLL_UNIT || dx < -SCROLL_UNIT || dy > SCROLL_UNIT || dy < -SCROLL_UNIT) {
					execJs("if (top.map.LT_P) {top.map.LT_P.ScrollImage(" + dx + "," + dy + ");}");
					dx = 0;
					dy = 0;
				}
			}
		} else if (event.getAction() == MotionEvent.ACTION_UP && event.getHistorySize() <= 1) {
			// ズームバーを表示する
			invokeZoomPicker();
		}
		return true;
	}

	@Override
	public boolean zoomIn() {
		execJs("if (top.map.LT_P) {top.map.LT_P.StartZooming('in'); top.map.LT_P.StopZooming();}");
		return true;
	}

	@Override
	public boolean zoomOut() {
		execJs("if (top.map.LT_P) {top.map.LT_P.StartZooming('out'); top.map.LT_P.StopZooming();}");
		return true;
	}

	private void execJs(String js) {
		loadUrl("javascript:" + js);
	}

	private ProgressDialog createStatusDialog(String text) {
		ProgressDialog prgDlg = new ProgressDialog(act);
		prgDlg.setMessage(text);
		prgDlg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
		prgDlg.show();
		return prgDlg;
	}

	/**
	 * 現在表示している地図の中心座標を設定します
	 * @param cx
	 * @param cy
	 */
	public void setMapCenter(float cx, float cy) {
		execJs("try {top.map.setMapCenter(" + cx + "," + cy + "); top.map.openMap(); } catch (e) {}");
	}

	/**
	 * 現在表示している地図の中心座標と表示縮尺を設定します
	 * @param cx
	 * @param cy
	 * @param scale
	 */
	public void setMapCenter(float cx, float cy, int scale) {
		execJs("try {top.map.setMapCenter(" + cx + "," + cy + "," + scale + "); top.map.openMap(); } catch (e) {}");
	}

	/**
	 * 現在表示している地図の中心座標と表示縮尺を取得します
	 * @return [0]:x,[1]:y,[2]:scale
	 */
	public float[] getCenter() {
		String mail = jsi.getPositionMailXY();
		String vals[] = mail.substring(3).split(",");

		float[] result = new float[3];
		result[0] = Float.parseFloat(vals[0]);
		result[1] = Float.parseFloat(vals[1]);
		result[2] = Float.parseFloat(vals[2]);

		return result;
	}

	/**
	 * JavaScriptインターフェイス
	 */
	public final class JSInterface {
		private boolean appMainFinished = false;
		private AlertDialog statusDlg;

		public JSInterface() {
			jsi = this;
		}

		public void onBodyStart() {
			statusDlg = createStatusDialog("接続しました ロード中です...");
		}

		public void onAppMainFinished() {
			if (statusDlg != null)	statusDlg.dismiss();

			statusDlg = createStatusDialog("地図の構築中です...");

			appMainFinished = true;
		}

		public void onBodyLoad() {
			if (! appMainFinished) {
				if (statusDlg != null)	statusDlg.dismiss();

				statusDlg = createStatusDialog("JavaScriptエラーです.実行し直してください");

			} else {
				// 地図の構築完了
				if (statusDlg != null)	statusDlg.dismiss();
			}
		}

		private String positionMail;
		private Semaphore positionMailToken;

		String getPositionMailXY() {
			positionMailToken = new Semaphore(1);

			try {
				// JSスレッドにリリースしてもらう分を確保する
				if (! positionMailToken.tryAcquire(SEMAPHORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) {
					throw new RuntimeException();
				}

				execJs("viewer.notifyPositionMailXY(top.map.getPositionMailXY())");

				// JSスレッドからのコールバックを待機する
				if (! positionMailToken.tryAcquire(SEMAPHORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) {
					throw new RuntimeException();
				}

				return positionMail;
				
			} catch (InterruptedException ex) {	
				throw new RuntimeException();
			}
		}

		public void notifyPositionMailXY(String mail) {
			positionMail = mail;
			// セマフォをリリースして,メインスレッドに完了を通知する
			positionMailToken.release();
		}
	}
}


© 2024 KMIソフトウェア