国土地理院の電子国土の地図を,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();
}
}
}