2016年7月14日木曜日

OpenLayers 3 を使ってみよう(その20:Open Street Map を表示させる)

 これは,OpenLayers 3 を使ってみよう(その19:その9からその18までのまとめ)からの続きになる。 OpenLayers 3 を使ってみよう(その0:はじめに:地理院地図を表示)に目次がある。 その13までのページでは OpenLayers v3.7.0 で書いてきたが,ここでは OpenLayers 3.16.0 で書かれている。
 前回(その19:その9からその18までのまとめ)は, その9:KML データで経路を描画するからその18:Marker アニメーションまでをまとめたものを示した。 今回は Open Street Map を表示させてみよう。

 Open Street Map(以降 OSM と略すこともある) はフリーの地図であり,ボランティアの協力で航空写真から地図を作っているものである。 Open Street Mapの表示 に行くと地図を見ることができる。 OSM の特徴は,等高線はないので,山の情報などはあまり期待できない。 逆に市街地は結構詳しく表示される(ように思う)。

 その OSM を OpenLayers で表示するには,以下のようにすればよい。 といって,特に難しいことがあるわけではなく,ol.layer.Tile として,ol.source.OSM を持つものを用意して,ol.Map の変数に加えればよい。

    var osm = new ol.layer.Tile({
        source: new ol.source.OSM()
    });

    var map = new ol.Map({
        target: document.getElementById('map_canvas'),
        layers: [osm],
        view: view,
        renderer: ['canvas', 'dom'],
        controls: ol.control.defaults().extend([new ol.control.ScaleLine()]),
        interactions: ol.interaction.defaults()
    });
ここで,ol.source.OSM は特に引数やオプションを指定していないが,必要なら,OpenLayers の API Documentsol.source.OSM の項を見てほしい。 また,この例では map の定義の中で
layers: [osm],
としているが,
map.addLayer(osm);
としても osm の layer を map に加える事ができる。

 osmの地図だけを表示してもあまり新しいことがないので,下記にその19で示したサンプルに Open Street Map を加えたものを示そう。ここではその19にあったような地理院地図と OSM を併記し,ラジオボタンスイッチで地図を入替えれるようにしている。それ以外はその19とほぼ同一だが,地図の入替えの時にベースとなる地図の不透明度を引き継ぐように変更している。

 まず,web ページのソースを載せよう。 web ページのソース部分は,灰色部分は web の基本的な要素であり,赤色太字部分が Open Street Map 表示関連の部分である。 赤色部分は JavaScript 関連の部分である。 青い部分は,センターマークや凡例と web のタイトルであり,茶色の部分はズーム関連である。 水色の部分はマーカーの吹出し関連であり,緑色部分は不透明度変更に関連した部分, 紫色の部分はアニメーション関連であり, オレンジ色の部分は距離に関連する部分である。 その他が黒色となっている。 説明はソースコードに下に書こう。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<!-- Written by Matsup 2016/07/11 -->
<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" type="text/css" href="http://openlayers.org/en/v3.16.0/css/ol.css">
<script src="http://openlayers.org/en/v3.16.0/build/ol.js"></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: 240px;
       margin-top: 0px;  margin-left: 0px;
       background-image:url(./o3cjmap_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(./o3cjmap_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: OSM with cyberJ map</title>
<script src="ol3ex20.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:30px; 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;不透明度:
  <a title="decrease opacity" href="javascript: changeOpacity(-0.2);">&lt;&lt;</a>
  <span id="opacity_control">1.0</span>
  <a title="increase opacity" href="javascript: changeOpacity(0.2);">&gt;&gt;</a></b>

  <input type="radio" name="select_map" id="cyberjMap" value="0" checked>地理院地図
  <input type="radio" name="select_map" id="osmMap" value="1">OpenStreetMap

  <button id="kml_vector_visible" onclick="kml_vector_visible();" class="blue">ルート隠す</button>
  <button id="wp_vector_visible" onclick="wp_vector_visible();" class="blue">マーカー隠す</button>
  <button id="centerMarker_visible" onclick="centerMarker_visible();" class="blue">センター隠す</button>
  <button id="titleSymbol_visible" onclick="titleSymbol_visible();" class="blue">Title/凡例隠す</button>

 アニメ<button id="start_animation" class="boldred">Start</button>
  <label for="speed">
  speed:&nbsp;
  <input id="speed" type="range" min="10" max="999" step="10" value="50">
  </label>

 Zoom <button id="autoZoomButton" onclick="setCenterZoom();" class="boldblack">Fit</button>
  <button id="expandZoomButton" onclick="expandZoom(2, view.getCenter());" class="red">Expand</button>
  <button id="contractZoomButton" onclick="expandZoom(0.5, view.getCenter());" class="blue">Contract</button>
 <button id="getLengthButton" onclick="length_line();" class="boldblack">経路長</button>
  <span id="outStr"></span>
</div>
</body>
</html>
この web ページのソースの多くはその19のものとほぼ同じなので,そちらを見てほしい。 今回の Open Street Map に関連した部分は赤色太字部分になっている。 ここでは地理院地図と OSM の切り替えを行うために radio ボタンを配置している。 それ以外はその19 と同じとなっている(はず…)。

 以下に JavaScript を載せよう。 こちらもその19のものとほぼ同じで,Open Street Map に関連した部分を赤色太字にしている。
他の色は,灰色部分が変数や定数の定義であり, 青い部分は,センターマークや凡例に関連した部分, 茶色の部分はズーム関連である。 水色の部分はマーカーの吹出し関連であり, 緑色部分は不透明度変更に関連した部分となっている。 紫色の部分はアニメーション関連であり, オレンジ色の部分は距離計算に関連する部分である。 赤色の部分はマーカーやセンターマークなどの表示・非表示に関連する部分である。 その他が黒色となっている。 説明は 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 osm = null;        // Open Street Map 用の変数
// -------------------------------------------------------------------
var kml_vector = null; // KML ファイル用変数
var wp_vector = null;  // waypoint KML ファイル用変数
var kml_extent = null; // KML ファイルの領域範囲用変数
var current_extent = null; // 現在表示している extent
// -------------------------------------------------------------------
var kml_vector_visible_flag = true;
var wp_vector_visible_flag = true;
var centerMarker_visible_flag = true;
var titleSymbol_visible_flag = true;
// -------------------------------------------------------------------
var kmlVectorVisibleButton = null;
var wpVectorVisibleButton = null;
var centerMarkerVisibleButton = null;
var titleSymbolVisibleButton = null;
var cyberjMapButton = null;
var osmMapButton = null;
// -------------------------------------------------------------------
// for animation demo
var styles = null;       // styles for geoMarker
var animating = false;   // flag animating or not
var speed, now;
var speedInput = null;   // speed control Element
var startButton = null;  // start button Element
// -------------------------------------------------------------------
var routeCoords = null;  // route の座標配列
var routeLength = null;  // route の長さ
var geoMarker = null;    // 移動する点のマーカー
var vectorLayer = null;  // geoMarker を入れる vector Layer
var moveFeature = null;  // アニメーション用の関数用の変数
// -------------------------------------------------------------------
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  = 5;   // ズームの最小値(最も広い範囲)
var MaxZoom  = 19;  // ズームの最大値(最も狭い範囲)
var MinResolution  = 40075016.68557849/256/Math.pow(2, MaxZoom); // 最小解像度
var MaxResolution  = 40075016.68557849/256/Math.pow(2, MinZoom); // 最大解像度
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');
// -------------------------------------------------------------------
// for animation 以下の変数は init_map() 内で指定しないと値が入らない。
    speedInput = document.getElementById('speed');
    startButton = document.getElementById('start_animation');
    kmlVectorVisibleButton = document.getElementById('kml_vector_visible');
    wpVectorVisibleButton = document.getElementById('wp_vector_visible');
    centerMarkerVisibleButton = document.getElementById('centerMarker_visible');
    titleSymbolVisibleButton = document.getElementById('titleSymbol_visible');
    cyberjMapButton = document.getElementById('cyberjMap');
    osmMapButton = document.getElementById('osmMap');
// -------------------------------------------------------------------
// 表示用の view 変数の定義
// projection はデフォルトの EPSG:3857(球面メルカトル図法)となっている。
    view = new ol.View({maxZoom: MaxZoom, minZoom:MinZoom});
// Open Street Map の変数
    osm = new ol.layer.Tile({
        source: new ol.source.OSM()
    });
// 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 データ
    kml_vector = new ol.layer.Vector({ source: new ol.source.Vector({ url: kml_url, format: new ol.format.KML({ showPointNames: false }) }) });
// マーカーの KML データ
    wp_vector = new ol.layer.Vector({ source: new ol.source.Vector({ url: kml_url2, format: new ol.format.KML({ showPointNames: false }) }) });
// -------------------------------------------------------------------
// 中央にセンターマーカー
    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'),
        loadTilesWhileAnimating: true,
        layers: [osm, 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) {
            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());
    osm.setVisible(false);

    view.setCenter(ol.proj.transform([center_lon, center_lat], "EPSG:4326", "EPSG:3857"));
    view.setZoom(initZoom);
    document.getElementById('opacity_control').innerHTML = initOpacity.toFixed(1);
// -------------------------------------------------------------------
    cyberjMapButton.onclick = function() {
        cyberJ.setVisible(true);
        osm.setVisible(false);
        changeOpacity(0);
    };
    osmMapButton.onclick = function() {
        cyberJ.setVisible(false);
        osm.setVisible(true);
        changeOpacity(0);
    };
// -------------------------------------------------------------------
// kml_vector に対する処理は,以下のようにイベント処理に入れないと,データが読まれずにうまくいかない。
    kml_vector.once('change',function() {
        kml_extent = kml_vector.getSource().getExtent();
        view.fit(kml_extent, map.getSize());
        current_extent = ol.extent.buffer(kml_extent,0); // duplicate kml_extent to current_extent

        var features = kml_vector.getSource().getFeatures(); // 通常の JavaScript の array
        for (var j=0; j < features.length; j++) {
            var coordArray = features[j].getGeometry().getCoordinates(); // geometry 変数の中の座標を配列へ(メルカトル座標系)
            if (j == 0) { routeCoords = coordArray; }
                   else { routeCoords = routeCoords.concat(coordArray); } // 全ルートの座標配列を結合
        }
        routeLength = routeCoords.length;

        geoMarker = new ol.Feature({ type: 'geoMarker', geometry: new ol.geom.Point(routeCoords[0]) });
        styles = { 'geoMarker': new ol.style.Style({
            image: new ol.style.Circle({ radius: 7, snapToPixel: false, fill: new ol.style.Fill({color: 'black'}), stroke: new ol.style.Stroke({ color: 'white', width: 2 }) }\
)
            })
        };
        vectorLayer = new ol.layer.Vector({
            source: new ol.source.Vector({ features: [geoMarker] }),
            style: function(feature) {
                // hide geoMarker if animation is active
                if (animating && feature.get('type') === 'geoMarker') { return null; }
                return styles[feature.get('type')];
            }
        });
        map.addLayer(vectorLayer);
    }); // kml_vector.once('change',function()
// -------------------------------------------------------------------
    moveFeature = function(event) {
        var vectorContext = event.vectorContext;
        var frameState = event.frameState;

        if (animating) {
            var elapsedTime = frameState.time - now; // here the trick to increase speed is to jump some indexes on lineString coordinates
            var index = Math.round(speed / 1000 * elapsedTime / 2000 * routeLength); // 2000 --> 2sec で全行程
            if (index >= routeLength) { stopAnimation(true); return; }
            var currentPoint = new ol.geom.Point(routeCoords[index]);
            var feature = new ol.Feature(currentPoint);
            vectorContext.drawFeature(feature, styles.geoMarker);
// ----------------
            if (!(ol.extent.containsCoordinate(current_extent, routeCoords[index]))) { expandZoom(1,routeCoords[index]); }
            speed = speedInput.value; // update speed
// ----------------
        }
        map.render(); // tell OL3 to continue the postcompose animation
    };
// -------------------------------------------------------------------
    startButton.addEventListener('click', startAnimation, false);
} // 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)); // 最大値と最小値の範囲を超えないように
    if (document.getElementById("cyberjMap").checked) { cyberJ.setOpacity(newOpacity); } else { osm.setOpacity(newOpacity); }
    document.getElementById('opacity_control').innerHTML = newOpacity.toFixed(1); // opacity の数字の表示書き換え
}
// *******************************************************************
function kml_vector_set_invisible() {
    kml_vector_visible_flag = false;
    kml_vector.setVisible(false);
    kmlVectorVisibleButton.textContent = "ルート表示";
    kmlVectorVisibleButton.setAttribute("class","red");
}
function kml_vector_set_visible() {
    kml_vector_visible_flag = true;
    kml_vector.setVisible(true);
    kmlVectorVisibleButton.textContent = "ルート隠す";
    kmlVectorVisibleButton.setAttribute("class","blue");
}
function kml_vector_visible() {
    if (kml_vector_visible_flag) { kml_vector_set_invisible(); }
                            else { kml_vector_set_visible(); }
}

function wp_vector_set_invisible() {
    wp_vector_visible_flag = false;
    wp_vector.setVisible(false);
    wpVectorVisibleButton.textContent = "マーカー表示";
    wpVectorVisibleButton.setAttribute("class","red");
}
function wp_vector_set_visible() {
    wp_vector_visible_flag = true;
    wp_vector.setVisible(true);
    wpVectorVisibleButton.textContent = "マーカー隠す";
    wpVectorVisibleButton.setAttribute("class","blue");
}
function wp_vector_visible() {
    if (wp_vector_visible_flag) { wp_vector_set_invisible(); }
                           else { wp_vector_set_visible(); }
}

function centerMarker_set_invisible() {
    centerMarker_visible_flag = false;
    document.getElementById('centerMarker').style.display = "none";
    centerMarkerVisibleButton.textContent = "センター表示";
    centerMarkerVisibleButton.setAttribute("class","red");
}
function centerMarker_set_visible() {
    centerMarker_visible_flag = true;
    document.getElementById('centerMarker').style.display = "block";
    centerMarkerVisibleButton.textContent = "センター隠す";
    centerMarkerVisibleButton.setAttribute("class","blue");
}
function centerMarker_visible() {
    if (centerMarker_visible_flag) { centerMarker_set_invisible(); }
                              else { centerMarker_set_visible(); }
}

function titleSymbol_set_invisible() {
    titleSymbol_visible_flag = false;
    document.getElementById('title-fig').style.display = "none";
    document.getElementById('symbols').style.display = "none";
    titleSymbolVisibleButton.textContent = "Title/凡例表示";
    titleSymbolVisibleButton.setAttribute("class","red");
}
function titleSymbol_set_visible() {
    titleSymbol_visible_flag = true;
    document.getElementById('title-fig').style.display = "block";
    document.getElementById('symbols').style.display = "block";
    titleSymbolVisibleButton.textContent = "Title/凡例隠す";
    titleSymbolVisibleButton.setAttribute("class","blue");
}
function titleSymbol_visible() {
    if (titleSymbol_visible_flag) { titleSymbol_set_invisible(); }
                             else { titleSymbol_set_visible(); }
}
// *******************************************************************
// ヒュベニの公式を使った距離計算
// 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
    for (var j=0; j < features.length; 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"); }
        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 = "  L = "+(Math.floor(lineLength)/1000)+" [km]";
}
// *******************************************************************
function setCenterZoom() {
    view.fit(kml_extent, map.getSize());
    current_extent = ol.extent.buffer(kml_extent,0); // duplicate kml_extent to current_extent
}
// *******************************************************************
function expandZoom(factor, center) {
    var cur_reso = view.getResolution();
    var new_reso = cur_reso/factor;
    new_reso = Math.min(new_reso, MaxResolution);
    new_reso = Math.max(new_reso, MinResolution);
    view.setCenter(center);
    view.setResolution(new_reso);
    current_extent = view.calculateExtent(map.getSize());
}
// *******************************************************************
function startAnimation() {
// ボタンはトグルになっているので,if (animating) が必要になる。
    if (animating) {
        stopAnimation(false);
    } else {
        animating = true;
        now = new Date().getTime();
        speed = speedInput.value; // max = 1000
        startButton.textContent = 'Cancel';
        startButton.setAttribute("class","boldblue");
        geoMarker.setStyle(null); // hide geoMarker
// ----------------
        kml_vector_set_visible();
        wp_vector_set_invisible();
        setCenterZoom(); // set extent to default
        expandZoom(2,routeCoords[0]); // set start point to the center of extent
// ----------------
        map.on('postcompose', moveFeature);
        map.render();
    }
}
// *******************************************************************
function stopAnimation(ended) {
    animating = false;
    startButton.textContent = 'Start';
    startButton.setAttribute("class","boldred");
    // if animation cancelled set the marker at the beginning
    var coord = ended ? routeCoords[routeLength - 1] : routeCoords[0];
    /** @type {ol.geom.Point} */ (geoMarker.getGeometry()).setCoordinates(coord);
    if (! ended) { setCenterZoom(); }
    map.un('postcompose', moveFeature); //remove listener
}
// *******************************************************************
 Open Street Map に関連するものとしては,global 変数の定義に cyberjMapButton と osmMapButton がカラ(null)で定義されている。 これは複数の関数で使用するために global 変数として定義しているものである。 その次は init_map() 関数の中で,上記の cyberjMapButton と osmMapButton を web 要素で id = cyberjMap のラジオボタンと id = osmMap のラジオボタンに割り当てている。 また,その少し下で ol.source.OSM() を source に持つ ol.vector.Tile が定義されている。

 ol.Map クラスの変数 map の定義の中では,osm Layer が加えられている。 一方で,起動時は地理院地図だけの表示にするために,センターやズームの指定の直前に osm Layer は osm.setVisible(false); として,表示しないようにしている。 そのすぐ下では,cyberjMapButton.onclick ... として,ラジオボタンがクリックされた際の処理を記載している。 ここでは,cyberJ 用のボタンがクリックされたら地理院地図のみの表示とし,osm ボタンがクリックされたら Open Street Map のみ表示するようにしている。 また,不透明度を引き継ぐために,changeOpacity(0); を実行している。これは,画面上に表示されている不透明度の数値を変えないで不透明度を設定し直すことを意味している。

 実はこれぐらいの追加しか行っていない。 しかし,地理院地図と Open Street Map を選べるので,OpenLayers は結構便利に使えると思っている。
「その20」のサンプルに具体例を載せているので, 具体的な表示を見てみて欲しい。(ちなみにサンプルページはアクセスログを取るルーチンを組み込んでいます)

その21:Highcharts でルートの標高グラフを表示に続く

0 件のコメント: