SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine(コードジン) DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Yahoo! Open Local Platform Hacks

YOLP Hacks: APIの使い方
~現在位置の常時表示/地図をなぞって距離を計測

Yahoo! Open Local Platform Hacks 第2回

地図をなぞって距離を測定する

 地図を使っていて「ここからここまではどのくらいの距離があるのかな」と思ったことはありませんか。ところが距離を測る手段は、なぜか地図サービスには意外なほどに用意されていません。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に任せ、ぜひ素敵なソリューションをたくさん生み出してください。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
Yahoo! Open Local Platform Hacks連載記事一覧

もっと読む

この記事の著者

河合 太郎(ヤフー株式会社)(カワイ タロウ(ヤフーカブシキガイシャ))

「Yahoo! Open Local Platform(略称:YOLP)」の企画・開発を担当。地図の実験サイト「LatLongLab」では企画・開発・デザインの一人三役をこなす。地図、位置情報、自転車、料理、写真、絵画など趣味多数。TwitterIDは@inuro。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/5907 2011/05/11 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー