2014年9月16日火曜日

OpenLayers 3 を使ってみよう(その13:センターマークと凡例の表示・KML による経路+マーカーの場合)

これはOpenLayers 3 を使ってみよう(その12:KML+マーカーで zoom の自動調整)からの続きである。
OpenLayers 3 を使ってみよう(その0:はじめに:地理院地図を表示)に目次がある。
ここでは OpenLayers 3.7.0 を使っている。
 前回(その12:KML+マーカーで zoom の自動調整)では, KML ファイルから作った経路に対して,地図の表示範囲を自動で調整できるようにした。 今回は,地図の中心にセンターマークを配置し,マーカーの凡例も表示してみよう。 さらに,それらの表示・非表示を切り替えるボタンを付けて,不必要な時には消せるようにしてみよう。 KML ファイルについての説明はOpenLayers 3 を使ってみよう(番外2:KML ファイルのフォーマットについて)に書いているので,そちらを見てみて欲しい。

 センターマークや凡例など,地図上の座標(緯度,経度)に関係なく画面上に同じ位置に止まっていて欲しい画像を表示するには, ol.control を使うのがいいらしい。 通常のマーカーの場合は,地図上を移動すると地図と一緒に動くので,地図上の座標(緯度,経度)で位置を指定しなくてはいけない。 そのような場合には ol.Overlay を用いるのがいいらしい。

 ここでは,ol.control としてセンターマークや凡例の画像を地図上に表示している。 OpenLayers 3 では,表示の位置や使う Icon の画像の指定などは,css で指定しないといけない。 なので,web ページのヘッダー部分にある <style> で指定をしている。

 まず,いつものように以下に web ページのソースを載せる。 web ページのソース部分は,赤色部分が JavaScript 関連の部分であり,灰色部分は web の基本的な要素である。 青い部分は,センターマークや凡例と web のタイトルである。 水色の部分はマーカーの吹出し関連であり,緑色部分は不透明度変更に関連した部分, 紫色の部分は JavaScript を呼び出すボタン類であり, オレンジ色の部分は zoom の変更や距離に関連する部分である。 その他が黒色となっている。 説明はソースコードに下に書こう。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta http-equiv="content-style-type" content="text/css">
<meta http-equiv="content-script-type" content="text/javascript">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" href="http://openlayers.org/en/v3.7.0/css/ol.css" type="text/css">
<script src="http://openlayers.org/en/v3.7.0/build/ol.js" type="text/javascript"></script>
<style type="text/css">
   div.fill {width: 100%; height: 100%;}
   body {padding: 0; margin: 0;}
   html, body, #map {height: 100%; width: 100%;}

   .center-marker {
       position: absolute;
       top: 50%; left: 50%;
   }
   .center-marker a {
       display: block;
       height: 32px; width: 32px;
       margin-top: -16px;  margin-left: -16px;
       background-image:url(../cj_map/crosshairs.png);
   }
   .title-fig {
       position: absolute;
       top: 0px; left: 40px;
   }
   .title-fig a {
       display: block;
       height: 21px; width: 163px;
       margin-top: 0px;  margin-left: 0px;
       background-image:url(./cjmap_title.png);
   }
   .symbols {
       position: absolute;
       top: 100%; left: 100%;
   }
   .symbols a {
       display: block;
       height: 144px; width: 120px;
       margin-top: -180px;  margin-left: -120px;
       background-image:url(./cjmap_icons.png);
   }

   .ol-attribution {
     padding: 3px;  position: absolute;  background-color:#ffffff;
     background-color:rgba(230,255,255,0.7);
     right: 3px;  bottom:5px;  font-size:12px;
   }
   .ol-attribution ul { padding: 0px;  line-height: 14px;  margin: 0px; }
   .ol-attribution li { line-height: inherit;  display: inline;  list-style: none outside none; }

   .ol-zoom .ol-zoom-out { margin-top: 202px; }
   .ol-zoomslider { background-color: transparent; top: 2.3em; }
   .ol-touch .ol-zoom .ol-zoom-out { margin-top: 212px; }
   .ol-touch .ol-zoomslider { top: 2.75em; }

   .ol-popup { display: none; position: absolute; background-color: white;
     -moz-box-shadow: 0 1px 4px rgba(0,0,0,0.2);
     -webkit-filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2));
     filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2));
     padding: 5px; border-radius: 10px; border: 1px solid #cccccc; bottom: 24px; left: -51px; }
   .ol-popup:after, .ol-popup:before { top: 100%; border: solid transparent; content: " ";
                                       height: 0; width: 0; position: absolute; pointer-events: none; }
   .ol-popup:after  { border-top-color: white;   border-width: 10px; left: 48px; margin-left: -10px; }
   .ol-popup:before { border-top-color: #cccccc; border-width: 11px; left: 48px; margin-left: -11px; }
   .ol-popup-closer { text-decoration: none; position: absolute; top: 2px; right: 8px; }
   .ol-popup-closer:after { content: " ✖ "; }

   button.boldblack { color:black; font-weight:bold; }
   button.red { color:red; }
   button.boldred { color:red; font-weight:bold; }
   button.blue { color:blue; }
   button.boldblue { color:blue; font-weight:bold; }
</style>
<title>OpenLayers 3 Example: KML Layer Visible/Invisible Switch</title>
<script src="ol3ex13.js" type="text/javascript"></script>
<script src="../jquery.min.js" type="text/javascript"></script>
</head>

<body onload="init_map()">
<div id="map_canvas" style="width: 100%; height: 97%; position:absolute; top:25px; left:0px; font-size:100%;">
  <div id="popup" class="ol-popup">
    <a href="#" id="popup-closer" class="ol-popup-closer"></a>
    <div id="popup-content"></div>
  </div>
</div>

<div style="font-size:85%">
  <b>&nbsp;不透明度・0.2ずつ変更:
    <a title="decrease opacity" href="javascript: changeOpacity(-0.2);">&lt;&lt;</a>
    <span id="opacity_control">0.5</span>
    <a title="increase opacity" href="javascript: changeOpacity(0.2);">&gt;&gt;</a></b>

    ルート<button id="kml_vector_visible" onclick="kml_vector_visible();" class="blue">表示</button>
  <button id="kml_vector_invisible" onclick="kml_vector_invisible();" class="red">非表示</button>
   マーカー<button id="wp_vector_visible" onclick="wp_vector_visible();" class="blue">表示</button>
  <button id="wp_vector_invisible" onclick="wp_vector_invisible();" class="red">非表示</button>
   センター<button id="centerMarker_visible" onclick="centerMarker_visible();" class="blue">表示</button>
  <button id="centerMarker_invisible" onclick="centerMarker_invisible();" class="red">非表示</button>
   タイトル・凡例<button id="titleSymbol_visible" onclick="titleSymbol_visible();" class="blue">表示</button>
  <button id="titleSymbol_invisible" onclick="titleSymbol_invisible();" class="red">非表示</button>

   <button id="autoZoomButton" onclick="setCenterZoom();" class="boldblue">Zoom 自動設定</button>
  <span id="outStr2"></span>
   <button id="getLengthButton" onclick="length_line();" class="boldblack">経路長の表示</button>
  <span id="outStr"></span>
   <span id="outStr3"></span>
</div>
</body>
</html>
 web ページ上の変更点は,センターマークや地図のタイトル,凡例用のスタイル(css)を増やしている点である。 それぞれセンターマーク,タイトル,凡例用に,2個ずつスタイルを記述している。 この中で,表示する場所(position),大きさ,画像の URL などを記載している。 position は,センターマークは画面中央,タイトルは左上,凡例は右下に表示するように指定している。 場所の指定は画像の左上の角の場所が指定されるので,画像がいい位置に来るように margein-top と margin-left にマイナスの数字を入れて位置を調整している。 display がすべて block にしてあるのは,最初から表示されるためにであり,ここを none にすると表示はされない。 それ以外の設定は特に変更はしていない。
 注意点として JavaScript の中で使いたいので jQuery (jquery.min.js) を読み込ませている。 ここでは jQuery はサーバーにダウンロードしてきたものを読み込ませている。

 次に JavaScript を載せよう。 色は,灰色部分が変数や定数の定義である。 赤色部分は距離に関連しており, 青い部分は,センターマークや凡例に関連した部分である。 水色の部分はマーカーの吹出し関連であり,緑色部分は不透明度変更に関連した部分, 紫色の部分は表示・非表示のボタン類であり, オレンジ色の部分は zoom の変更などに関連する部分である。 その他が黒色となっている。 説明は web ページのソースと同じくこの下に書いておく。 ちなみに,これまでと変わって,ルートは六甲全山縦走のコースにしてある。
// -------------------------------------------------------------------
// Define a namespace for the application.
window.app = {};
var app = window.app; // 中心マークや凡例の表示用

var map = null;        // map 変数
var view = null;       // view 変数
var cyberJ = null;     // 地理院地図用の変数
var kml_vector = null; // KML ファイル用変数 1
var wp_vector = null;  // waypoint KML ファイル用変数 2

var center_lon = 135.083273333333; // 表示中心の経度(デフォルトは始点の経度)
var center_lat = 34.63374; // 表示中心の緯度(デフォルトは始点の緯度)

var kml_url  = "work/map_data20160430.kml";
var kml_url2 = "work/wp_data20160430.kml";

var initZoom = 15; // ズームの初期値
var MinZoom  = 6;   // ズームの最小値(最も広い範囲)
var MaxZoom  = 21;  // ズームの最大値(最も狭い範囲)

var initPrecision = 8; // 座標表示の小数点以下の桁数の初期値
var initOpacity = 1.0; // 不透明度の初期値
var gMaxOpacity = 1.0; // 不透明度の最大値
var gMinOpacity = 0.0; // 不透明度の最小値
// -------------------------------------------------------------------
// ヒュベニの公式で緯度・経度から距離を求めるための定数
var long_r = 6378137.000;     // [m] 長半径
var short_r = 6356752.314245; // [m] 短半径
var rishin = Math.sqrt((long_r * long_r - short_r * short_r)/(long_r * long_r)); // 第一離心率
var a_e_2 = long_r * (1-rishin * rishin);  // a(1-e^2)
var pi = 3.14159265358979;    // Pi
// *******************************************************************
function init_map() {
// 以下の DOM の定義は,init_map() の中に入れないとだめだった。
    var container = document.getElementById('popup');
    var content   = document.getElementById('popup-content');
    var closer    = document.getElementById('popup-closer');

// 表示用の view 変数の定義
    view = new ol.View({maxZoom: MaxZoom, minZoom:MinZoom});
// cyberJ の opacity をいじるために,cyberJ という変数に入れている。
    cyberJ = new ol.layer.Tile({
        opacity: initOpacity,
        source: new ol.source.XYZ({
            attributions: [ new ol.Attribution({ html: "<a href='http://maps.gsi.go.jp/development/ichiran.html' target='_blank'>国土地理院</a>" }) ],
            url: "http://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png",  projection: "EPSG:3857"
        })
    });
// 経路の KML データ
// v3.11.0 以降の場合は new ol.format.KML() を new ol.format.KML({ showPointNames: false }) とする。
    kml_vector = new ol.layer.Vector({ source: new ol.source.Vector({ url: kml_url, format: new ol.format.KML() }) });
// マーカーの KML データ
// v3.11.0 以降の場合は new ol.format.KML() を new ol.format.KML({ showPointNames: false }) とする。
    wp_vector = new ol.layer.Vector({ source: new ol.source.Vector({ url: kml_url2, format: new ol.format.KML() }) });
// -------------------------------------------------------------------
// 中央にセンターマーカー
    app.addCenterMarker = function(opt_options) {
        var options = opt_options || {};
        var anchor = document.createElement('a');
        var element = document.createElement('div');
        element.className = 'center-marker ol-unselectable';
        element.id = 'centerMarker';
        element.appendChild(anchor);
        ol.control.Control.call(this, { element: element, target: options.target });
    };
    ol.inherits(app.addCenterMarker, ol.control.Control);
// -------------------------------------------------------------------
// 左上にタイトル
    app.addTitleFig = function(opt_options) {
        var options = opt_options || {};
        var anchor = document.createElement('a');
        var element = document.createElement('div');
        element.className = 'title-fig ol-unselectable';
        element.id = 'title-fig';
        element.appendChild(anchor);
        ol.control.Control.call(this, { element: element, target: options.target });
    };
    ol.inherits(app.addTitleFig, ol.control.Control);
// -------------------------------------------------------------------
// 右下に凡例
    app.addSymbols = function(opt_options) {
        var options = opt_options || {};
        var anchor = document.createElement('a');
        var element = document.createElement('div');
        element.className = 'symbols ol-unselectable';
        element.id = 'symbols';
        element.appendChild(anchor);
        ol.control.Control.call(this, { element: element, target: options.target });
    };
    ol.inherits(app.addSymbols, ol.control.Control);
// -------------------------------------------------------------------
// 地図をクリックした際に,停留点の情報を表示するための overlay 変数(popup 用)
    var overlay = new ol.Overlay({ element: container });
// 地図変数 (map 変数) の定義。地理院地図を表示するように指定している
    map = new ol.Map({
        target: document.getElementById('map_canvas'),
        layers: [cyberJ, kml_vector, wp_vector],
        view: view,
        overlays: [overlay],
        renderer: ['canvas', 'dom'],
        controls: ol.control.defaults().extend([new ol.control.ScaleLine()]),
        interactions: ol.interaction.defaults()
    });

    function displayFeatureInfo(pixel, coordinate) {
        var features = [];
        map.forEachFeatureAtPixel(pixel, function(feature, wp_layer) {
            features.push(feature);
        });
        if (features.length > 0) {
            var info = [];
            info.push('<div id="wp_desc" style="font-size:12px; width:215px">'+features[0].get('name')+features[0].get('description')+'</div>');
            overlay.setPosition(coordinate);
            content.innerHTML = info[0];
            container.style.display = 'block';
        } else {
            content.innerHTML = '';
            container.style.display = 'none';
        }
    };
    map.on('click', function(evt) { displayFeatureInfo(evt.pixel, evt.coordinate); });

// マーカー上でアイコンの表示を変更するイベントハンドラー, jQuery が必要
    $(map.getViewport()).on('mousemove', function(e) {
        var pixel = map.getEventPixel(e.originalEvent);
        var hit = map.forEachFeatureAtPixel(pixel, function(feature, layer) { return true; });
        if (hit) { map.getTarget().style.cursor = 'pointer'; }
            else { map.getTarget().style.cursor = ''; }
    });
// popup を閉じるためのイベントハンドラー
    closer.onclick = function() {
        container.style.display = 'none';
        closer.blur();
        return false;
    };

    map.addControl(new ol.control.ZoomSlider());
    map.addControl(new app.addCenterMarker());
    map.addControl(new app.addTitleFig());
    map.addControl(new app.addSymbols());

    view.setCenter(ol.proj.transform([center_lon, center_lat], "EPSG:4326", "EPSG:3857"));
    view.setZoom(initZoom);
    document.getElementById('opacity_control').innerHTML = initOpacity.toFixed(1);
} // init_map()
// *******************************************************************
// 地理院地図 (var cyberJ) の opacity(この場合は不透明度) を変える
function changeOpacity(opacity) {
    var newOpacity = (parseFloat(document.getElementById('opacity_control').innerHTML) + opacity).toFixed(1); // 新しい opacity の値を求める
    newOpacity = Math.min(gMaxOpacity, Math.max(gMinOpacity, newOpacity)); // 最大値と最小値の範囲を超えないように
    cyberJ.setOpacity(newOpacity); // 地理院地図の opacity の変更
    document.getElementById('opacity_control').innerHTML = newOpacity.toFixed(1); // opacity の数字の表示書き換え
}
function directSetOpacity(opacity) {
    cyberJ.setOpacity(opacity);  document.getElementById('opacity_control').innerHTML = opacity.toFixed(1);
}
// *******************************************************************
function kml_vector_visible()   { kml_vector.setVisible(true); }
function kml_vector_invisible() { kml_vector.setVisible(false); }
function wp_vector_visible()    { wp_vector.setVisible(true); }
function wp_vector_invisible()  { wp_vector.setVisible(false); }
function centerMarker_visible()   { document.getElementById('centerMarker').style.display = "block"; }
function centerMarker_invisible() { document.getElementById('centerMarker').style.display = "none"; }
function titleSymbol_visible()    { document.getElementById('title-fig').style.display = "block";  document.getElementById('symbols').style.display = "block"; }
function titleSymbol_invisible()  { document.getElementById('title-fig').style.display = "none";   document.getElementById('symbols').style.display = "none"; }
// *******************************************************************
// ヒュベニの公式を使った距離計算
// 2点間の距離
function dist_2pts(lon0, lat0, lon1, lat1) {
    lon0 = lon0 * pi / 180;  lat0 = lat0 * pi / 180; // in radian
    lon1 = lon1 * pi / 180;  lat1 = lat1 * pi / 180; // in radian
    var d_lon = lon1 - lon0;
    var d_lat = lat1 - lat0;
    var ave_lat = (lat1+lat0)/2;
    var Wx = Math.sqrt(1-rishin * rishin * Math.sin(ave_lat) * Math.sin(ave_lat));
    var Mx = a_e_2 /Wx/Wx/Wx;
    var Nx = long_r /Wx;
    var dum = (d_lat * Mx)*(d_lat * Mx) + (d_lon* Nx * Math.cos(ave_lat)) * (d_lon* Nx * Math.cos(ave_lat)); // square of distance
    return Math.sqrt(dum);
}
// ===================================================================
// 経路の長さを求めて表示する
function length_line() {
    var lineLength = 0;
    var features = kml_vector.getSource().getFeatures(); // 通常の JavaScript の array
    var lengf = features.length;
    for (var j=0; j < lengf; j++) {
        var geometry = features[j].getGeometry(); // featureOverlay の中のgeometry(例:ol.geom.LineString )
        var coordArray = geometry.getCoordinates(); // geometry 変数の中の座標を配列へ(メルカトル座標系)
        for (var i=0; i < coordArray.length; i++) { coordArray[i] = ol.proj.transform(coordArray[i],"EPSG:3857", "EPSG:4326"); }
        for (var i=0; i < (coordArray.length-1); i++) {
            lineLength = lineLength + dist_2pts(coordArray[i][0],coordArray[i][1],coordArray[i+1][0],coordArray[i+1][1]); 
        }
    }
    document.getElementById("outStr").innerHTML = "&nbsp;&nbsp;L = "+(Math.floor(lineLength)/1000)+" [km]";
}
// *******************************************************************
function setCenterZoom() {
    var features = kml_vector.getSource().getFeatures(); // 通常の JavaScript の array
    var lengf = features.length;
    document.getElementById("outStr2").innerHTML = ""; // 文字列のクリア
// feature が1個以上,feature[0] が2点以上あれば,以下の処理を行う
    if ((lengf > 0) && (features[0].getGeometry().getCoordinates().length >1)) {
        var coordArray = new Array();
        for (var j=0; j < lengf; j++) {
            coordArray = coordArray.concat(features[j].getGeometry().getCoordinates()); // 各 feature に含まれる線分座標の配列を追加
        }
        var lineStrings = new ol.geom.LineString(coordArray);
        view.fit(lineStrings, map.getSize());

// 経路の占める範囲を表示
        var coord0 = (features[0].getGeometry().getCoordinates())[0];
        coord0 = ol.proj.transform(coord0,"EPSG:3857", "EPSG:4326");
        var lonMax = coord0[0];
        var lonMin = coord0[0];
        var latMax = coord0[1];
        var latMin = coord0[1];

        for(var j=0; j < lengf; j++) {
            var coordArray = features[j].getGeometry().getCoordinates(); // geometry 変数の中の座標を配列へ(メルカトル座標系)
            for (var i=0; i < coordArray.length; i++) {
                coordArray[i] = ol.proj.transform(coordArray[i],"EPSG:3857", "EPSG:4326");
                lonMax = Math.max (lonMax, coordArray[i][0]);  lonMin = Math.min (lonMin, coordArray[i][0]);
                latMax = Math.max (latMax, coordArray[i][1]);  latMin = Math.min (latMin, coordArray[i][1]);
            }
        }

        var size_x = dist_2pts(lonMin, latMin, lonMax, latMin); // 経路範囲の横の長さ
        var size_y = dist_2pts(lonMin, latMin, lonMin, latMax); // 経路範囲の縦方向の長さ
        document.getElementById("outStr2").innerHTML = "範囲:"+(Math.round(size_x)/1000)+" x "+(Math.round(size_y)/1000)+" [km]  ";
    }
}
// *******************************************************************
 まず,色がついていない OpenLayers 3 の基本的な部分だが,これまでにも書いているので,細かい点は省略しよう。 わからない時は,OpenLayers 3 を使ってみよう(その0:はじめに:地理院地図を表示)にある目次から該当するものを探して説明を見てみて欲しい。 おおまかには,地図として地理院地図を持ちており,'map_canvas' という id を持つ web 要素に地図が描かれる。 controls(スイッチみたいなもの)や interactions(地図や地図上の物体へのなんらかのアクション(動作))もとりあえず基本的な物がデフォルトで設定されている。

 青色部分は ol.control を使ったセンターマークや凡例の表示に関連する部分である。 センターマーク,地図のタイトル,凡例とあるが,どれも基本的に同じなので, センターマークについて見てみよう。 まず,変数定義の部分で,var app = window.app; として,app という変数を定義している。 これは,センターマークの表示に使う関数を割り当てるために,ダミーで作った変数(インスタンス)である。 これ自体にはあまり意味は無い。

 そして,app.addCenterMarker = function(opt_options) でセンターマーク用の ol.control を設定している。 まず,app という変数(インスタンス)に対して,addCenterMarker という関数を定義している。 しかし,何か地図を変更するコントローラーではないので,具体的にはほとんどなにもしていない。 やっているのは,新しく <a> と <div> という web 要素を作り, その <div> element に対して id と class name を与えている。 id はセンターマークの表示・非表示の設定の際に DOM で指定するために加えてある。 そして,<div> に子要素 anchor を加え,関数が呼ばれたらこれらの web 要素を表示している。 最後に,ol.inherits を使って,ol.control.Control クラスのサブクラスとして app.addCenterMarker を登録している。 そのため関数としての app.addCenterMarker は新しいクラスを作るコンストラクターになるみたい。 (実は,いまいちわかってないので,もしかしたら言ってることは間違ってるかも。でも,これでちゃんと表示されてるから,よしとしましょう)。

 そして,最後に map.addControl(new app.addCenterMarker()); として map にこれらの control を追加している。 ここでちゃんと control として加えることができても,css で display を none にしていると,当初の表示はされない。 下の方にある表示・非表示の切替えの関数は,この display を block にするか,none にするかを使っている。 少なくとも,一度 map.addControl をしておかないと,センターマークなどを表示させることはできない。

 その他の部分についてみてみると,赤色部分はヒュベニの公式を使って,2点間の距離や経路の長さを求めている部分であり, OpenLayers 3 を使ってみよう(その8:ol.interaction.Draw 修正可能バージョンで表示領域の大きさの調整を行う)を見てみて欲しい。

 水色部分はマーカーの吹出し関連である。 詳しくはOpenLayers 3 を使ってみよう(その10:KML でマーカーを描画して吹出しをつける)を見てみて欲しい。

 緑色部分は不透明度の変更に関連である。 OpenLayers 3 を使ってみよう(その2:地図の不透明度を変える)に記載があるので見てみて欲しい。

 紫色部分は表示・非表示の切替に関する部分である。前々回に記載があるが,あまり難しくはないので,説明は省略しよう。

 オレンジ色部分は地図表示の自動調整に関連した部分である。 OpenLayers 3 を使ってみよう(その8:ol.interaction.Draw 修正可能バージョンで表示領域の大きさの調整を行う)に記載があるので見てみて欲しい。 ここではさらに,得られた経路が含まれる矩形の大きさを表示している。 経路に含まれる緯度と経度の最大,最小を求め,ヒュベニの公式を用いてそれらの間の距離を求めている。

追記オレンジ色部分だが,自動で表示範囲を調整するだけなら,getExtent() と fit() を使うと簡単にできる。
function setCenterZoom() {
   var extent = kml_vector.getSource().getExtent();
   map.getView().fit(extent, map.getSize()); // <== サンプルでは view.fit(extent, map.getSize()); としている。
}
これは,kml_vector という Layer の source (ここでは vector 形式) の範囲を getExtent() 関数で得ている。 そして,その範囲に合うように map の Size を fit() 関数で extent の値に合わせている。
 もし,経路が複数ある場合は,ol.extent.extend() 関数で範囲を広げればいいみた。 この関数はオブジェクト指向っぽくなく,通常の関数のように使うみたい。
function setCenterZoom() {
    var extent = kml_vector[0].getSource().getExtent();
    for(var k=0; k < max_kml; k++) {
       var extent2 = kml_vector[k].getSource().getExtent();
       ol.extent.extend(extent, extent2);
    }
    map.getView().fit(extent, map.getSize());
}


追記2) 最近,ふとうまくいった事があるので追記しよう。 それは何かというと,地図のページを読み込んだ際に,KML データの範囲に合わせて,自動で表示範囲を調節したかったのだが,なかなかうまくいっていなかった。 それができるようになった。ヒントは Google Maps API v3 で,表示範囲の自動調整ボタンを加えた時にどこかのサイトで見つけた方法だった。

 どうするかというと,地図を読み込んで KML データを地図に表示させた後で,イベント処理を使って KML データの範囲を取得し,地図の表示範囲を KML データの範囲に合わせる,という方法である。具体的には,まず,
var kml_vector = null; // KML ファイル用変数
var wp_vector = null;  // waypoint KML ファイル用変数 2
var kml_extent = null; // KML ファイルの領域範囲用変数
として,global な変数を変数定義のところに書いておく(ここでは青い字の部分が追加されたもの)。 そして,init_map(); 関数の最後に,以下を追加しておく。
    kml_vector.once('change',function() {
        kml_extent = kml_vector.getSource().getExtent();
        map.getView().fit(kml_extent, map.getSize()); // <== サンプルでは view.fit(kml_extent, map.getSize()); としている。
    });
ここでは,KML 経路データの変数 kml_vector に変化があれば,kml_extent というデータの範囲を示す変数に KML データの範囲を入力させ,その後,fit() 関数を使って,map の表示範囲を KML データの範囲に fit させている。 こうすると地図を読み込んだ直後に,うまく表示範囲を合わせてくる。 当初,イベント処理を使わずにコマンドを書いていたが,うまくいかなかった。 ところが Google Maps API v3 の例として,似たようにすればいい,とどこかに書いてあったので真似してみた次第である。 これは,KML 経路として kml_vector を指定しているが,当初,kml_vector の定義式を書いただけだと,実際の KML データは読み込まれていないみたい。 そのため,kml_extent を指定しても,そもそも KML データを読み込んでいないためにうまく範囲を調整できなかった。 しかし,イベント処理を使って,kml_vector に変化があれば一度だけ,としたので,KML データが実際に読み込まれた後でここの処理が実行されるようになったみたい。

ちなみに,init_map() の中で kml_extent に値を入れたので,ボタンを押して表示範囲を自動調節するルーチン (setCenterZoom())は,以下のように簡単になる。
function setCenterZoom() {
   map.getView().fit(kml_extent, map.getSize()); // <== サンプルでは view.fit(extent, map.getSize()); としている。
}


 以下に具体例を載せてある。
「その13」のサンプル(当初のサンプル)
「その13」のサンプル(シンブルなsetCenterZoom()関数を持つバージョン) (追記の内容を反映)
「その13」のサンプル(起動時に自動調整できるバージョン) (追記2の内容を反映)
具体的な表示を見てみて欲しい。(ちなみにサンプルページはアクセスログを取るルーチンを組み込んでいます)

その14:GPX による経路の表示・各点の通過時刻を知るに続く

0 件のコメント: