チュートリアル5
提供: svg2wiki
(版間での差分)
(→dynamicOSM_r11.html) |
(→dynamicOSM_r11.html) |
||
96行: | 96行: | ||
*<code>onload</code> 通常のwebAppと同様、このhtmlに関係するリソースの読み込み完了後実行される関数 | *<code>onload</code> 通常のwebAppと同様、このhtmlに関係するリソースの読み込み完了後実行される関数 | ||
− | **<code>svgMap.refreshScreen()</code> 伸縮スクロール以外のタイミングでDOMの再描画が必要な場合、[[解説書#refreshScreen|再描画を明示]]する必要があります。 | + | **<code>svgMap</code> [[解説書#svgMap|コアフレームワークインスタンス]] |
− | *** [[解説書#.E5.86.8D.E6.8F.8F.E7.94.BB.E3.81.AE.E5.88.B6.E9.99.90|参考情報]] | + | ***<code>svgMap.refreshScreen()</code> 伸縮スクロール以外のタイミングでDOMの再描画が必要な場合、[[解説書#refreshScreen|再描画を明示]]する必要があります。 |
+ | **** [[解説書#.E5.86.8D.E6.8F.8F.E7.94.BB.E3.81.AE.E5.88.B6.E9.99.90|参考情報]] | ||
*<code>preRenderFunction()</code> この関数を定義すると、画面の再描画の度に、その直前にこの関数が実行されます。 | *<code>preRenderFunction()</code> この関数を定義すると、画面の再描画の度に、その直前にこの関数が実行されます。 | ||
**<code>svgImagesProps</code> [[解説書#svgMap|webAppに紐づいたSVGコンテンツの各種情報]] | **<code>svgImagesProps</code> [[解説書#svgMap|webAppに紐づいたSVGコンテンツの各種情報]] | ||
***[[解説書#svgImageProps|解説書参照]] , [[解説書#.E3.83.97.E3.83.AD.E3.83.91.E3.83.86.E3.82.A3.E3.83.AA.E3.82.B9.E3.83.88|参照2]] | ***[[解説書#svgImageProps|解説書参照]] , [[解説書#.E3.83.97.E3.83.AD.E3.83.91.E3.83.86.E3.82.A3.E3.83.AA.E3.82.B9.E3.83.88|参照2]] | ||
***<code>svgImageProps.scale</code> SVGコンテンツの座標系の画面の座標系に対するスケール (注:地理座標系とは異なる) | ***<code>svgImageProps.scale</code> SVGコンテンツの座標系の画面の座標系に対するスケール (注:地理座標系とは異なる) | ||
− | + | **<code>svgMap.getGeoViewBox()</code> [[解説書#getGeoViewBox|表示領域を地理座標で取得]] | |
− | + | ||
**[[解説書#preRenderFunction|参考1]]、[[解説書#setPreRenderController|参考2]] | **[[解説書#preRenderFunction|参考1]]、[[解説書#setPreRenderController|参考2]] | ||
*<code>svgImage</code> [[解説書#svgImage|紐付けられたSVGMapコンテンツのDOM]] | *<code>svgImage</code> [[解説書#svgImage|紐付けられたSVGMapコンテンツのDOM]] |
2022年2月15日 (火) 08:38時点における版
目次 |
チュートリアル5 WebApp Layer タイルピラミッド
実際の動作は、こちらをクリック。
tutorial5.html
- SVGMapのコアプログラムファイル(SVGMapLv0.1_r17.js, SVGMapLv0.1_LayerUI2_r4.js)を読み込み、SVGMapの各種APIを利用可能にする。
- 地図表示部分を(DIVで)定義し、そこに表示するレイヤをまとめたSVGファイル(Containers.svg)を読み込む(上記SVGMapのコアプログラムにて自動的にVisibleになっているレイヤが表示される)。
- ズームアップ・ズームダウン・GPSの各ボタンの表示とクリック時の動作(SVGMapのコアプログラムのそれぞれのAPIを呼び出す)を定義。
- ズームアップボタン:svgMap.zoomup() APIを呼び出すことで地図をズームアップする。
- ズームダウンボタン:svgMap.zoomdown() APIを呼び出すことで地図をズームダウンする。
- GPSボタン:svgMap.gps() APIを呼び出すことで、現在地(PCやスマートフォンの位置、特定できる場合のみ)を中心にズームアップ表示する。
- 中心を表す十字マークを表示。
- 上記十字マークが示している地図上の緯度・経度の表示(実際には、地図の移動時に地図の中心の緯度・経度を表示する)。
<!DOCTYPE html> <html> <title>SVGMapLevel0.1-Rev14-Draft Tutorial5 DynamicContents</title> <!-- viewport 知表示領域を画面全体とする定義 --> <meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1.0,maximum-scale=1.0" /> <meta charset="UTF-8"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <!-- SVGMapのコアAPIの読み込み --> <script type="text/javascript" src="../js/SVGMapLv0.1_r14.js"></script> <body bgcolor="#ffffff" style="overflow:hidden;" > <!-- 地図SVGファイルを複数含む(このチュートリアルでは5ファイルのみ)コンテナファイル(Container.svg)の読み込み --> <div id="mapcanvas" data-src="Container.svg"></div> <div id="gui"> <!-- ズームアップボタン --> <img id="zoomupButton" style="left: 5px; top: 5px; position: absolute;" src="../img/zoomup.png" onclick="svgMap.zoomup()" width="20" height="20" /> <!-- ズームダウンボタン --> <img id="zoomdownButton" style="left: 5px; top: 25px; position: absolute;" src="../img/zoomdown.png" onclick="svgMap.zoomdown()" width="20" height="20" /> <!-- GPSボタン --> <img id="gpsButton" style="left: 5px; top: 45px; position: absolute;" src="../img/gps.png" onclick="svgMap.gps()" width="20" height="20" /> <!-- 画面右上に表示するタイトル --> <font color="blue" style="right: 5px; top: 5px; position: absolute;" >SVGMapLevel0.1 Rev14 Draft : Tutorial5 DynamicContents</font> <!-- 画面右下に表示する --> <font color="blue" style="right: 5px; bottom: 5px; position: absolute;" size="-2" >by SVGMap tech.</font> <!-- 中央に表示される十字マーク --> <img id="centerSight" style="opacity:0.5" src="../img/Xcursor.png" width="15" height="15"/> <!-- 画面左下に表示される十字マークの緯度・経度(タイトル) --> <font id="posCmt" size="-2" color="brown" style="left: 5px; bottom: 5px; position: absolute;">Lat,Lng:</font> <!-- 画面左下に表示される十字マークの緯度・経度(実際の値の初期表示) --> <font id="centerPos" size="-2" color="brown" style="left: 50px; bottom: 5px; position: absolute;" >lat , lng</font> </div> </body> </html>
Container.svg
- 表示する各レイヤ用のSVGファイルを読み込む(dynamicOSM_r10.svgのみを読み込んでいる)。
<?xml version="1.0" encoding="UTF-8"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:go="http://purl.org/svgmap/profile" viewBox="12300 -4600 2200 2200" > <globalCoordinateSystem srsName="http://purl.org/crs/84" transform="matrix(100.0,0.0,0.0,-100.0,0.0,0.0)" /> <!-- OpenStreetMap用SVGファイルを表示状態として読み込む --> <animation x="-30000" y="-30000" width="60000" height="60000" xlink:href="dynamicOSM_r10.svg" title="OpenStreetMap(Global)" class="basemap switch" visibility="visible"/> </svg>
dynamicOSM_r11.svg
外部のOpenStreetMapを表示するためのWebAppが紐付けられたSVG Mapコンテンツレイヤーファイル。
SVGMap.jsの持つWebApp Layer機構を活用します。
- SVGコンテンツのドキュメント要素(svg要素)のdata-controller属性でウェブアプリケーション(javascriptコードが載ったhtmlコンテンツ)を参照することで、SVGMapコンテンツのレイヤーにウェブアプリを紐づけます。
- #exec=hiddenOnLayerLoad ハッシュは、同ウェブアプリケーションを非表示状態で起動させます。解説書#レイヤー固有のUIの詳細を参照
- ウェブアプリでDOMを直接生成するので、コンテンツの中身はほとんど空になっています。
<?xml version="1.0" encoding="UTF-8"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-42.8202042942663, -49.9999999999999, 513.842451531196, 600" data-controller="dynamicOSM_r11.html#exec=hiddenOnLayerLoad" > <globalCoordinateSystem srsName="http://purl.org/crs/84" transform="matrix(100,0,0,-100,0,0)" /> </svg>
dynamicOSM_r11.html
dynamicOSM_r11.svgに紐付けられたwebApp(レイヤー固有のUI)です。レイヤーに紐付けられたwebAppでは、独自のAPIが利用できます。解説書
OpenStreetMap(OSM)は縮尺に応じたピラミッド状の256×256ピクセルのタイルに分割された、ピラミッド状のイメージを配信しています。(OSM Slippy map tilenames, OSM ZoomLevels)
これを使用して、表示範囲とズームレベルに適したタイルをOpenStreetMapのサーバから動的に取得・表示する機能(Level of Detail)を実装します。
Note: ここで使用しているOpenStreetMapのタイルはいわゆるウェブメルカトル図法上での均等メッシュタイルとなっています。一方、このチュートリアルで使用しているSVGMapコンテンツは正距方位図法(Plate Caree:経度緯度をそのままX,Y平面に展開した図法、業務や技術系の分野でよく使われる)を用いています。そのため図法変換が必要になりますが、本チュートリアルでは簡単化のためにタイルの1次変換だけで済ませています。日本列島レベルぐらいの小縮尺では少し図法の違いによるずれが見えるかもしれません。(SVGMap.jsではより正確な図法変換にも対応しています。)
onload
通常のwebAppと同様、このhtmlに関係するリソースの読み込み完了後実行される関数svgMap
コアフレームワークインスタンス
preRenderFunction()
この関数を定義すると、画面の再描画の度に、その直前にこの関数が実行されます。svgImagesProps
webAppに紐づいたSVGコンテンツの各種情報svgMap.getGeoViewBox()
表示領域を地理座標で取得- 参考1、参考2
svgImage
紐付けられたSVGMapコンテンツのDOM
getTileSet
現在のズームレベルと表示領域において、表示すべきタイルをリストアップする関数
<!doctype html> <html> <head> <meta charset="utf-8"></meta> <title>OpenStreerMap Dynamic Layer</title> </head> <script> // Dynamic OpemStreetMap Layer for SVGMap Sample for SVGMapLevel0 > r10 // Programmed by Satoru Takagi // Copyright (C) 2013 by Satoru Takagi @ KDDI CORPORATION // // License: // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License version 3 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see (http://www.gnu.org/licenses/) . // iframe化を想定した動的レイヤーのプロトタイプ // (JavaScriptをインポートSVGコンテンツに置くことができる。) // 地図データとしては、OpenStreetMapを利用(比較的容易に他にも置き換えられる) // // // このコードの動作環境では、以下があらかじめ設定される // document:このドキュメント自身 // svgImage:このドキュメントに紐づいたSVGMapコンテンツ // svgMap.getGeoViewBox(): 地理的なビューボックス // svgImageProps:このドキュメントに紐づいたSVGMapコンテンツの各種プロパティ // svgImageProps.scale: スケール(画面座標に対する、このsvgコンテンツの座標のスケール) // // 2013/01/24 : 1st ver. // 2022/01/31 : WebApp layerに移植 // このファイルの読み込み時に実行する onload = function(){ // このスクリプトが読み込まれた直後、refreshScreen()を呼ぶことで、 //下記preRenderFunctionが初回実行される svgMap.refreshScreen(); } function preRenderFunction(){ // 再描画直前に実行されるコールバック関数 var level = 8; // ズームレベルを計算(3から18) var level = Math.floor( Math.LOG2E * Math.log(svgImageProps.scale) + 7.5); if (level > 18 ){ level = 18; } else if ( level < 3 ){ level = 3; } // この地図の地理座標におけるviewBox内表示させる、tileのXYとそのHashKeyを取得する var tileSet = getTileSet( svgMap.getGeoViewBox() , level ) // 現在読み込まれているimageというタグ名を持った(地図のタイルごとのイメージ)要素を取得 console.log("tileSet:",tileSet); var currentTiles = svgImage.getElementsByTagName("image"); // 取得できた各タイル分以下を繰り返し、既に読み込み済みのものは再利用、表示範囲外のものは削除する for ( var i = currentTiles.length - 1 ; i >= 0 ; i-- ){ var oneTile = currentTiles[i]; var qkey = oneTile.getAttribute("metadata"); if ( tileSet[qkey] ){ // すでにあるのでスキップさせるフラグ立てる。 tileSet[qkey].exist = true; } else { // ないものなので、消去 oneTile.parentNode.removeChild(oneTile); } } // 表示させるタイル分以下を繰り返し、読み込まれていないファイルを読込み要素に加える for ( var tkey in tileSet ){ if ( ! tileSet[tkey].exist ){ var addTile = getTile( tileSet[tkey].x , tileSet[tkey].y , level , this.CRS ); svgImage.getElementsByTagName("svg")[0].appendChild(addTile); } } } // 指定された場所のタイル(分割された地図イメージ)を取得 function getTile( tileX , tileY , level , crs ){ // tileX、tileYの座標、levelのズームレベルのタイルのURLを取得。 var tileURL = getURL( tileX , tileY , level); // タイルのSVGにおけるbboxを得る var tLatLng = XY2latLng( tileX * tilePix , tileY * tilePix, level ); var tSvg = svgMap.transform( tLatLng.lng , tLatLng.lat , crs ); var tLatLngBR = XY2latLng( tileX * tilePix + tilePix , tileY * tilePix + tilePix , level ); var tSvgBR = svgMap.transform( tLatLngBR.lng , tLatLngBR.lat , crs ); tSvg.width = tSvgBR.x - tSvg.x; // 効率悪い・・改善後回し tSvg.height = tSvgBR.y - tSvg.y; // 取得するタイル要素を作成し、各属性をセットする。 var cl = svgImage.createElement("image"); cl.setAttribute("x" , tSvg.x); cl.setAttribute("y" , tSvg.y); cl.setAttribute("width" , tSvg.width); cl.setAttribute("height" , tSvg.height); cl.setAttribute("xlink:href" , tileURL.URL); cl.setAttribute("metadata" , tileURL.Key); return ( cl ); } // 指定された地図座標geoViewBoxに、levelのズームレベルの地図を表示する場合に、必要なタイルのXYのセットを返却する function getTileSet( geoViewBox , level ){ var TileSet = new Object(); if ( geoViewBox.y + geoViewBox.height > 85.05113 ){ geoViewBox.height = 85.05113 - geoViewBox.y; } if ( geoViewBox.y < -85.05113 ){ geoViewBox.y = -85.05113; } // 指定エリアの、tileのXYとそのHashKeyを返却する var tlxy = latLng2XY( geoViewBox.y + geoViewBox.height , geoViewBox.x , level ); var tileTLxy = XY2TileXY( tlxy ); var brxy = latLng2XY( geoViewBox.y , geoViewBox.x + geoViewBox.width, level ); var tileBRxy = XY2TileXY( brxy ); // 必要な高さ・幅分のタイル個数分以下を繰り返す for ( var i = tileTLxy.y ; i <= tileBRxy.y ; i++ ){ for ( var j = tileTLxy.x ; j <= tileBRxy.x ; j++ ){ // タイルのXYとズームレベルからHashKeyを取得する var qkey = getKey( j, i, level); // 上記で取得したHashKeyごとに、必要なタイル情報を設定する TileSet[qkey] = new Object(); TileSet[qkey].x = j; TileSet[qkey].y = i; } } return ( TileSet ); } // 緯度・経度からXYに変換 function latLng2XY( lat , lng , lvl ){ var size = lvl2Res(lvl); var sinLat = Math.sin(lat * Math.PI / 180.0); var pixelX = (( lng + 180.0 ) / 360.0 ) * size; var pixelY = (0.5 - Math.log((1 + sinLat) / (1.0 - sinLat)) / (4 * Math.PI)) * size; return { x : pixelX , y : pixelY } } // XYからタイルのXYに変換 function XY2TileXY( xy ){ var tileX = Math.floor(xy.x / tilePix); var tileY = Math.floor(xy.y / tilePix); return { x : tileX , y : tileY } } var tilePix = 256; // ズームレベルからタイルの一片のサイズを返却 function lvl2Res( lvl ){ var j = 1; for(var i = 0 ; i < lvl ; i++){ j = j * 2; } return ( j * tilePix ); } // XYから緯度・経度に変換 function XY2latLng( px , py , lvl ){ var size = lvl2Res(lvl); var x = ( px / size ) - 0.5; var y = 0.5 - ( py / size); var lat = 90 - 360 * Math.atan(Math.exp(-y * 2 * Math.PI)) / Math.PI; var lng = 360 * x; return{ lat : lat , lng : lng } } var sva = new Array( "a" , "b" , "c" ); var svNumb = 0; var culture ="en-US"; var bingRoadSearchPart = ".jpeg?g=849&mkt=" + culture + "&shading=hill"; // タイルのXYとズームレベルからURLを返却する function getURL( tx , ty , lvl ){ // XYとズームレベルからHashKeyを取得 var tile_ans = getKey( tx , ty , lvl ); // OpenStreetMapのURLを組み立てる var mapServerURL = "http://" + sva[svNumb] + ".tile.openstreetmap.org/" + lvl + "/" + tx + "/" + ty + ".png"; // 複数の同様のサーバを順次切り替えながら使用することで、地図イメージ取得時の負荷分散を行う。 ++ svNumb; if ( svNumb > 2 ){ svNumb = 0; } return { URL : mapServerURL , Key : tile_ans } } // HashKeyを生成し返却する function getKey(tx , ty , lvl){ return ( tx + "_" + ty + "_" + lvl ); } </script> <body> <h3>OpenStreerMap Dynamic Layer</h3> </body> </html>