// Description:
// Dynamic OpemStreetMap Layer for SVGMap Sample for SVGMapLevel0 > r10
//
// Programmed by Satoru Takagi
// 
// License: (MPL v2)
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

//
// 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);
	cl.setAttribute("data-mercator-tile" , "true");

	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&amp;mkt=" + culture + "&amp;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;
}
