国土地理院の電子国土の地図を,Androidで表示するサンプルコード(Java).ドラッグによるスクロールとズームに対応しています.
Androidで電子国土を表示(Java)
Android版の注意事項
- もともと国土地理院の電子国土 は,Andriodでの動作を保証していません
- Androidへの対応は,カモランド作者が個人的に行っているものであり,国土地理院は無関係です
- そのため,電子国土のAndriodでの動作については,国土地理院に問い合わせないようにしてください
- また,今後の国土地理院側の仕様変更には,できるだけ追随するつもりですが,対応しきれずに動作しなくなる可能性があります
以上を了解の上,このページの内容を利用してください.
概要
国土地理院の電子国土の地図を,Androidアプリで無理矢理表示するサンプルコードです.
- タッチパッドのドラッグによる,地図のスクロール
- ズーム
に対応しています.(ただしスクロールは,結構もたつきます orz) → なお,この方法をアプリとして実装したものが,地図ロイドです.
ソースコード
アクティビティ
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();
}
}
}