地図をなぞって距離を測定する
地図を使っていて「ここからここまではどのくらいの距離があるのかな」と思ったことはありませんか。ところが距離を測る手段は、なぜか地図サービスには意外なほどに用意されていません。YOLPを使って実装してみましょう。
以下がサンプルです。ChromeまたはFirefoxで確認してみてください。右上の「start measuring」ボタンを押すと、地図上でのドラッグでスクロールの代わりに線が引かれ、左上に距離が表示されます。渋谷駅を出て東急ハンズまで歩くと500mちょっとあるようです。
先のサンプルと同様、<あなたのアプリケーションID>の部分はご自分で取得されたアプリケーションIDに書き換えてお試しください。
<!DOCTYPE html>
<html lang="ja">
<head>
<title>YOLP drawing sample</title>
<meta charset="UTF-8">
</head>
<body>
<div style="width:640px; height:480px; position:relative; margin:8px;">
<div id="map" style="width:640px; height:480px;"></div>
<canvas id="canvas" width="640" height="480" style="position:absolute; top:0px; left:0px; width:640px; height:480px; z-index:2; display:none;"></canvas>
<div id="result" style="position:absolute; top:8px; left:8px; font-size:24pt; font-weight:bold; display:none;"></div>
<button id="measure" style="position:absolute; top:8px; right:8px; z-index:3; display:none;">start measuring</button>
</div>
<script type="text/javascript" src="http://js.api.olp.yahooapis.jp/OpenLocalPlatform/V1/jsapi?appid=<あなたのアプリケーションID>"></script>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript">
$(document).ready(function($){
var map = new Y.Map("map", {configure:{scrollWheelZoom:true}}),
canvas = $('#canvas'),
ctx = canvas[0].getContext('2d'),
result = $('#result'),
button = $('#measure'),
isDrawMode = false,
isDrawing = false,
prev = null,
prevll = null,
distance = 0;
ctx.strokeStyle = 'rgba(0,128,255,1.0)';
ctx.lineWidth = 4;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
canvas.bind("mousedown", function(e){
ctx.clearRect(0, 0, canvas.width(), canvas.height());
prev = {x:e.layerX, y:e.layerY};
prevll = map.fromContainerPixelToLatLng(prev);
distance = 0;
result.empty();
isDrawing = true;
return false;
});
canvas.bind("mousemove", function(e){
if(isDrawing){
var current = {x:e.layerX, y:e.layerY},
currentll = map.fromContainerPixelToLatLng(current);
ctx.beginPath();
ctx.moveTo(prev.x, prev.y);
ctx.lineTo(current.x, current.y);
ctx.stroke();
ctx.closePath();
distance += prevll.distance(currentll);
result.html(distance >= 1.0 ? Math.floor(distance * 10) / 10 + 'km' : Math.floor(distance * 1000) + 'm');
prev = current;
prevll = currentll;
}
});
$(document).bind("mouseup", function(e){
if(isDrawing) isDrawing = false;
});
button.bind("click", function(){
if(isDrawMode){
canvas.hide();
result.hide();
button.html('start measuring');
isDrawMode = false;
}
else{
ctx.clearRect(0, 0, canvas.width(), canvas.height());
canvas.show();
result.empty();
result.show();
button.html('stop');
isDrawMode = true;
}
}).show();
map.drawMap(new Y.LatLng(35.691052, 139.701258), 16, Y.LayerSetId.NORMAL);
});
</script>
</body>
</html>
canvasの準備
線はYOLPの上にcanvasを被せてそこに描くことにします。positionをabsolute、z-indexを2に設定して、canvasタグをmapと完全に重なるように配置し、displayにnoneを設定して初期状態では隠しておきます。他に、測定結果を表示するためのdiv(id="result")、測定モードに切り替えるためのbutton(id="measure")も配置します。
<div style="width:640px; height:480px; position:relative; margin:8px;"> <div id="map" style="width:640px; height:480px;"></div> <canvas id="canvas" width="640" height="480" style="position:absolute; top:0px; left:0px; width:640px; height:480px; z-index:2; display:none;"></canvas> <div id="result" style="position:absolute; top:8px; left:8px; font-size:24pt; font-weight:bold; display:none;"></div> <button id="measure" style="position:absolute; top:8px; right:8px; z-index:3; display:none;">start measuring</button> </div>
canvasは最初に初期化しておきます。ここでは2D描画用のコンテキストを取得し、線の色と幅、接続の形を設定しています。
var canvas = $('#canvas'),
ctx = canvas[0].getContext('2d');
ctx.strokeStyle = 'rgba(0,128,255,1.0)';
ctx.lineWidth = 4;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
モード切り替えボタン
右上のボタンを押してモードを切り替えるための実装です。測定モードでは、地図上でドラッグを行った場合、地図のスクロールではなく線が描画される必要があります。
この切り替えを行う実装はさまざまなやり方がありますが、ここではもっとも簡単に、地図のdivに重ねたcanvasにイベントを全部食わせることで地図にイベントを渡さないという方法を用いました。ボタンが押されるとcanvasの中身をクリアし、canvasを表示します。これ以降地図の上のイベントはすべてcanvasが引き受けます。もう一度押されるとcanvasを非表示にし、再び地図が操作できるようになります。
$('#measure').bind("click", function(){
if(isDrawMode){
canvas.hide();
isDrawMode = false;
}
else{
ctx.clearRect(0, 0, canvas.width(), canvas.height());
canvas.show();
isDrawMode = true;
}
});
mousedownハンドラの記述
測定モードでは、マウスが押し下げられた位置を起点として距離を測定します。距離は2点間の距離の値を単純に合算したものを結果とします。軌跡を描画するために直前のピクセル座標をprevに、距離を計算するために直前の緯度経度値をprevllに退避します。
地図上のあるピクセル座標を緯度経度に変換するためには、Y.MapのfromContainerPixelToLatLng()メソッドを使用します。
canvas.bind("mousedown", function(e){
if(isDrawMode){
ctx.clearRect(0, 0, canvas.width(), canvas.height());
prev = {x:e.layerX, y:e.layerY};
prevll = map.fromContainerPixelToLatLng(prev);
distance = 0;
result.empty();
isDrawing = true;
}
return false;
});
mousemoveハンドラの記述
測定のメイン部分です。直前の位置から現在位置まで線を描画し、また距離を計算して結果として表示します。
2点間の距離を計算するためには、Y.LatLngのdistanceメソッドを使用します。
canvas.bind("mousemove", function(e){
if(isDrawing){
var current = {x:e.layerX, y:e.layerY},
currentll = map.fromContainerPixelToLatLng(current);
ctx.beginPath();
ctx.moveTo(prev.x, prev.y);
ctx.lineTo(current.x, current.y);
ctx.stroke();
ctx.closePath();
distance += prevll.distance(currentll);
result.html(distance >= 1.0 ? Math.floor(distance * 10) / 10 + 'km' : Math.floor(distance * 1000) + 'm');
prev = current;
prevll = currentll;
}
});
mouseupハンドラの記述
mouseupハンドラは単純に描画中フラグを下ろすだけです。ただし地図の外側でマウスが離された場合に対処するために、canvasではなくdocumentのハンドラとして記述しています。
$(document).bind("mouseup", function(e){
if(isDrawing) isDrawing = false;
});
まとめ
このサンプルには、
- ピクセル座標から緯度経度座標に変換する方法
- 2点間の距離を計算する方法
- マウスイベントを地図に取られないように切り離す方法
などが含まれています。
おわりに
いかがでしたでしょうか。
YOLPそれ自体よりもむしろ周辺のコードの解説の方が多くなってしまったくらいですが、裏を返せば地図自体はそれだけ簡単に扱えるということです。
複雑な部分はYOLPに任せ、ぜひ素敵なソリューションをたくさん生み出してください。

