2011年4月6日水曜日

GPSの経路ログ表示にGoogle Maps API V3 を使う (その8:InfoWindowの改良)

その1:KML 形式ファイルを作る に目次があります。

前回のその7:センターマークの改良で終わったと思っていたが,いろいろ調べているうちに,InfoWindow の表示に配列を使わない,ってのに成功したので,折角だから書いておくことにした。今度こそ最後の予定。基本的には今までの知識の総まとめ,という感じになっている。詳しくは html ファイルの下の説明を読んでほしい。

まずは web ページの html ファイルを表示する。今回は念の為に全部表示しよう。そのために適当に色をつけてある。色は目的ごとにグループ分けしている(つもり)。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Route : EXAMPLE</title>
<link href="http://code.google.com/apis/maps/documentation/javascript/examples/default.css" 
rel="stylesheet" type="text/css">
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
    var wps = [
        ['経路1:出発点',35.0,136.5,1,'motorcycling',27,2,
        '<div id="wp_desc" style="font-size:12px; width:220px">
        <div id="wp_name">経路1:出発点</div><div id="wp_address">三重県のどっか</div> </div>'],
        ['経路1:Stay Point',35.0,136.55,2,
        'grocerystore',18,4,'<div id="wp_desc" style="font-size:12px; width:220px">
        <div id="wp_name">経路1:Stay Point</div><div id="wp_address">三重県のはず</div> </div>']
    ];
    var map;
    var centerLatLng = new google.maps.LatLng(35.0, 136.5);
    var ctaLayer = new google.maps.KmlLayer('http://xxxx.yyyy.zzzz.jp/map_data.kml');
    var cmLayer  = new google.maps.KmlLayer(
           'http://hippo.matsup.mydns.jp/motorcycle/centerMark.kml', {preserveViewport:true});
    var markersArray = [];
    var InfoWindowX = new google.maps.InfoWindow;
    var Elevation_flag = false;
    var trackMarkersArray = [];

    function initialize() {
        var myOptions = {
            zoom: 11,
            center: centerLatLng,
            mapTypeId: google.maps.MapTypeId.ROADMAP,
            scaleControl: true,
            scaleControlOptions: { position: google.maps.ControlPosition.BOTTOM_CENTER }
        }
        map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
        ctaLayer.setMap(map);

        setWPMarkers(map, wps);
        cmLayer.setMap(map);
        google.maps.event.addListener(map, 'click', function(mouseEvent) {
            setTrackMarkers(map, mouseEvent.latLng);
        });
    }
// --------------------------------------------------
    function clearCenterMark() {
        cmLayer.setMap(null);
    }
    function showCenterMark() {
        cmLayer.setMap(map);
    }
// --------------------------------------------------
    function setWPMarkers(map, locations) {
        for (var i = 0; i < locations.length; i++) {
            var points = locations[i];
            var icon_url = 'http://maps.gstatic.com/intl/ja_jp/mapfiles/ms/micons/'
                            +points[4]+'.png';
            var icon_shadow_url = 
                'http://maps.gstatic.com/intl/ja_jp/mapfiles/ms/micons/'+points[4]+'.shadow.png';
            if (points[4].match(/pushpin/i)) {
                icon_shadow_url = 
                    'http://maps.gstatic.com/intl/ja_jp/mapfiles/ms/micons/pushpin_shadow.png';
            }
            var image = new google.maps.MarkerImage(icon_url, new google.maps.Size(32,32),
                        new google.maps.Point(0,0),  new google.maps.Point(points[5],(32-points[6])));
            var shadow = new google.maps.MarkerImage(icon_shadow_url,  new google.maps.Size(59,32),
                         new google.maps.Point(0,0),  new google.maps.Point(points[5],(32-points[6])));
            var shape = { coord: [16, 0, 0, 32, 32, 32], type: 'poly' };
            var myLatLng = new google.maps.LatLng(points[1], points[2]);
            var marker = new google.maps.Marker({
                position: myLatLng,  map: map,  shadow: shadow,
                icon: image,  shape: shape,  title: points[0],  zIndex: points[3]
            });
            markersArray.push(marker);
            attachMessage(marker, points[7]);
        }
    }
    function clearIcons() {
        if (markersArray) {
            for (i in markersArray) {
                markersArray[i].setMap(null);
            }
        }
    }
    function showIcons() {
        if (markersArray) {
            for (i in markersArray) {
                markersArray[i].setMap(map);
            }
        }
    }
// --------------------------------------------------
    function attachMessage(marker, message) {
        google.maps.event.addListener(marker, 'click', function() {
            InfoWindowX.setContent(message);
            if (Elevation_flag) {
                getElevation(marker.getPosition(),InfoWindowX);
            }
            InfoWindowX.open(map,marker);
         });
    }
    function clearInfoWindow() {
        InfoWindowX.close();
    }
// --------------------------------------------------
    function getElevation(latlng, infowindow) {
        var geocoder = new google.maps.Geocoder();
        geocoder.geocode({
            latLng: latlng
        }, function(results, status) {
            if (status == google.maps.GeocoderStatus.OK) {
                if (results[0].geometry) {
                    var message = infowindow.getContent();
                    var address = results[0].formatted_address.replace(/^日本, /, '');
                    var infoMessage = message+"<div id=\"geocoder\" style=\"font-size:12px;\">
                            <b>Google Info: </b><br>"+address +"</div>";
                    infowindow.setContent(infoMessage);
                }
            }
            else { alert("Geocoder Service ERROR !!"); }
        });

        var locations = [latlng];
        var elevation = new google.maps.ElevationService();
        elevation.getElevationForLocations({
            locations: locations
        }, function(results, status) {
            if (status == google.maps.ElevationStatus.OK) {
                if (results[0].elevation) {
                    var message = infowindow.getContent();
                    var altitude = results[0].elevation;
                    var infoMessage = message
                          +"<div id=\"altitude\" style=\"font-size:12px;\"><b>Google Info: </b>";
                    infoMessage = infoMessage 
                            +"Altitude: " +altitude +" [m]<br>" + latlng + "<br> </div>";
                    infowindow.setContent(infoMessage);
                }
            }
            else { alert("Elevation Service ERROR !!"); }
        });
    }

    function clearElevation() {
        Elevation_flag = false;
    }
    function showElevation() {
        Elevation_flag = true;
    }
// --------------------------------------------------
    function setTrackMarkers(map, myLatLng) {
        clearTrackIcons();
        var icon_url = 'http://maps.gstatic.com/intl/ja_jp/mapfiles/ms/micons/ylw-pushpin.png';
        var icon_shadow_url = 'http://maps.gstatic.com/intl/ja_jp/mapfiles/ms/micons/pushpin_shadow.png';
        var image = new google.maps.MarkerImage(icon_url, new google.maps.Size(32,32),
                    new google.maps.Point(0,0),  new google.maps.Point(10,31));
        var shadow = new google.maps.MarkerImage(icon_shadow_url,  new google.maps.Size(59,32),
                     new google.maps.Point(0,0),  new google.maps.Point(10,31));
        var trackmarker = new google.maps.Marker({
            position: myLatLng,  map: map,  shadow: shadow,
            icon: image,  title: 'Track Info',  zIndex: 999
        });
        trackMarkersArray.push(trackmarker);
        setTrackInfoWindow(trackmarker);
    }
    function setTrackInfoWindow(trackmarker) {
        var trackinfowindow = new google.maps.InfoWindow({content: ''});
        getElevation(trackmarker.getPosition(),trackinfowindow);
        trackinfowindow.open(map,trackmarker);
    }
    function clearTrackIcons() {
        if (trackMarkersArray) {
            for (i in trackMarkersArray) {
                trackMarkersArray[i].setMap(null);
            }
        }
    }
    function showTrackIcons() {
        if (trackMarkersArray) {
            for (i in trackMarkersArray) {
                trackMarkersArray[i].setMap(map);
            }
        }
    }
// --------------------------------------------------
</script>
</head>

<body bgcolor="#D0D0D0" onload="initialize()">
<div>
<input style="color:blue" onclick="clearCenterMark();" type=button value="Center消去"> 
<input style="color:red" onclick="showCenterMark();" type=button value="Center表示"> 
<input style="color:blue" onclick="clearIcons();" type=button value="Icon消去"> 
<input style="color:red" onclick="showIcons();" type=button value="Icon表示"> 
<input style="color:black" onclick="clearInfoWindow();" type=button value="吹出し消去"> 
<input style="color:magenta" onclick="showElevation();" type=button value="吹出+Addr/Alt">
<input style="color:black" onclick="clearElevation();" type=button value="NO Addr/Alt"> 
<input style="color:black" onclick="clearTrackIcons();" type=button value="Pts Icon消去">
</div>
<div id="map_canvas" style="width: 100%; height: 95%; position:absolute; top:35px; left:0px"></div>
</body>
</html>

 まずは色付けの説明をしておこう。灰色の部分は,JavaScript と直接関係ない部分を表している。青色の部分アイコン (Marker関連部分を表し,赤色の部分吹出し(InfoWindowに関連する部分を表している。さらに,水色の部分高度と住所(Google GeocoderElevation Serviceを示し,オレンジ色の部分クリックした点の情報を得るためのルーチンを,紫色の部分センターマーク関連のルーチンを示している。

 各処理について説明したいが,長くなるし,煩雑になるので,以前の説明で済むところは以前の説明のリンクをはろう。

(1) 黒い部分(経路の地図を表示している)は,その2:KMLファイルの中身を地図に表示するを見てもらうのがいいと思う。
  Google Maps API v3 で KML ファイルから地図を表示する,部分についてのみ書いているので,わかりやすいと思う。

(2) 次にセンターマークについてだが,この例でのセンターマークは,経路とは別の独立した KML ファイルを用意して,
  それを地図上に表示している。詳しくはその7:センターマークの改良を見てほしい。

(3) アイコン (Markerに関しては,その3:アイコン(Marker)の表示を読んでほしい。
  コツとしては,作ったアイコンを global 変数として用意した Marker 用の配列に入れる,という点である。
  こうすることで,メモリのダブりもなくなり,それぞれに別々のアイコンを指定することができ,
  かつ,後で画面上から消したり,再び表示したりできるようにしやすい。

(4) さて,問題の吹出し(InfoWindowだが,その4:吹出し(InfoWindow)の表示と違うのは,
  global 変数として,配列ではなく, InfoWindowX という google.maps.InfoWindow クラスの変数を用意している。
  この定義のところで,var InfoWindowX = new google.maps.InfoWindow(); のようにカッコをつけてはいけない。
  カッコがあるとうまく働かなかった。カッコなしで定義しておくのがコツみたい。
   また,attachMessage として,アイコンを表示するルーチンとは独立したサブルーチンにしてあるのもコツである。
  アイコンを表示するルーチンの中に直接 attachMessage の中身を書くと,いつも特定の一個のアイコンの情報を表示しようとする。
  これは,JavaScript で,ローカルな変数っぽいのに,同じ変数名だと同じメモリを共有することに由来するみたい。
  (実はいまいちよくわかってない…)。

追記)この振る舞いについて説明してくれているサイトを見つけた。連載:Ajax時代のJavaScriptプログラミング再入門ってところ。具体的には第3回 変数の宣言とスコープにローカル変数について書いてあった。やっぱりわかってる人は違うねぇ。原因はブロック・レベルのスコープは存在しないってことにあるらしい。つまり,関数(サブルーチン)の中で定義された変数はローカル変数だけど,for 文の中で定義してもローカル変数にはならない,ということみたい。一度この連載をちゃんと読まないとあかんなぁ。

(5) 高度と住所(Google GeocoderElevation Serviceに関しては,その6:吹出しに高度と住所をいれる を読んでほしい。
  そこにも書いたがGoogle Maps APIで、あそ~ぶを参考にさせていただいたので,そちらを見る方がわかりやすいかもしれない。

(6) クリックした点の情報は,特に記述はしてないが,アイコン (Markerとほぼ同じ内容なので,
  その3:アイコン(Marker)の表示を読んで理解してみてほしい。

(7) 経路を表す KML ファイルはその1:KML 形式ファイルを作るに,
  センターマークを表す KML ファイルはその7:センターマークの改良に記載があるので,それぞれのページを見てみてほしい。

(8) 今回のサンプル地図がサンプルページmap3.html として置いてある。
  具体的な表示のされ方を見てほしい。

追記)2011年のGWにR439を走ってみた。そのルートマップとして,ここで述べたことを応用してみた。写真をいっぱい撮ったのでカメラマークだらけだが,「Icon消去」ボタンを押せば,ルートだけになる。また,拡大すると相対的にカメラマークはまばらになるので,見やすくなる。一度ご覧あれ。R439のルートマップ

 さて,今度こそ,このシリーズは終わりにしよう。頭にも書いたが,その1:KML 形式ファイルを作る に目次があるので,参考にしてほしい。次に Gooble Maps API v3 について書く時は,別のタイトルにすることにしようと思う。

0 件のコメント: