
function RouteDelta(map)
{
	this.map = map;

	this.encoded_points = "";
	this.pathDistance   = 0;
	this.linearDistance = 0;

	this.pathAry    = [];
	this.pathPoly   = null;
	this.zoom       = -1;
	this.maptype    = 0;
	
	// スクロール用変数
	this.panIndex   = 0;	// pathAry のインデックス値。pathAry.length 以上ならスクロールしてない状態。
	this.panIndex2  = -1;	// １つの線分の中のどこらへん？ のインデックス値
	this.panIndex2max = 0;	// 上記の終了値
	this.pan_dlat   = 0;	// １つの線分の変位量のlat
	this.pan_dlng   = 0;	// １つの線分の変位量のlng
	this.pan_dl     = 30;	// タイマごとに進む距離。ピクセル単位。隠しプロパティ？

	// 旧フォーマット用
	this.deltaHtml      = "";
	this.deltaText      = "";
	this.resolution = 100000;
	this.B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.';
	this.l = 0;	// RouteDelta_bb64()用：文字数
	this.s = '';	// RouteDelta_bb64()用：文字列(BASE64もどき)

	// スタート地点のアイコン
	this.baseIcon = new GIcon();
	this.baseIcon.shadow = "http://www.google.com/mapfiles/shadow50.png";
	this.baseIcon.iconSize = new GSize(20, 34);
	this.baseIcon.shadowSize = new GSize(37, 34);
	this.baseIcon.iconAnchor = new GPoint(9, 34);
	this.baseIcon.infoWindowAnchor = new GPoint(9, 2);
	this.baseIcon.infoShadowAnchor = new GPoint(18, 25);
}

function RouteDelta_initMapCenter()
{
	var center = this.pathAry[0];
	var boxPath;
	if ( this.pathAry.length >= 1 ) {
		// ルートが指定されてる場合
		if ( this.zoom == -1 ) {
			// 「ルートが収まる矩形」を計算
			boxPath = new GLatLngBounds(this.pathAry[0], this.pathAry[0]);
			for ( i=1; i<this.pathAry.length; i++ ) {
				boxPath.extend(this.pathAry[i]);
			}
			// これで、boxPath に「ルートが収まる矩形」が入りました。
		}
	}
	else {
		//boxPath = new GLatLngBounds(new GLatLng(30.98,129.55),new GLatLng(45.52,145.82));	// 日本本土（北海道～九州）
		boxPath = new GLatLngBounds(new GLatLng(33+26/60.0,130+51/60.0),new GLatLng(41+33/60.0,142+4/60.0));	// 本州
	}

	if ( this.zoom == -1 ) {
		// 「ルートが収まるズームレベル」を計算
		this.zoom = this.map.getBoundsZoomLevel(boxPath);
		center = boxPath.getCenter();
	}

	this.map.setCenter(center, this.zoom);
	this.map.setMapType(this.maptype);
	if ( 17<this.zoom ) {
		this.map.setZoom(this.zoom); // 航空写真かつズーム値が18以上の時は、呼ばないとダメ。setMapType を setCenter の前に呼ぶと落ちる。
	}
}
RouteDelta.prototype.initMapCenter = RouteDelta_initMapCenter;


function RouteDelta_addLatLng(latlng)
{
	var lat = latlng.lat();
	var lng = latlng.lng();
	// 誤差蓄積回避のため、丸めておく
	lat = lat.toFixed(5);
	lng = lng.toFixed(5);
	this.pathAry.push(new GLatLng(lat,lng));	// パスを追加する
	this.panIndex = this.pathAry.length;	// スクロール抑制

	this.pathRedraw();
}
RouteDelta.prototype.addLatLng = RouteDelta_addLatLng;

// 最後の点を削除
function RouteDelta_delete_last1()
{
	this.pathAry.pop();
	this.pathRedraw();
}
RouteDelta.prototype.delete_last1 = RouteDelta_delete_last1;

// 線を全て削除
function RouteDelta_delete_all_lines()
{
	this.pathAry = [];
	this.pathRedraw();
}
RouteDelta.prototype.delete_all_lines = RouteDelta_delete_all_lines;


// パスの再描画・距離再計算
function RouteDelta_pathRedraw()
{
	this.pathDistance = 0;
	this.linearDistance = 0;

	this.map.clearOverlays();	// とりあえず全部消す！
	if ( this.pathAry.length >= 1 ) {  // 1個以上じゃないとPolylineでエラーが出るので。
		// 前回の線を消して、書く
		this.pathPoly = new GPolyline(this.pathAry, "#ff0000");
		this.map.addOverlay(this.pathPoly);
		// 距離計算
		for ( i=1; i<this.pathAry.length; i++ ) {
			this.pathDistance += (this.pathAry[i-1]).distanceFrom(this.pathAry[i]);
		}
		this.linearDistance = this.pathAry[0].distanceFrom(this.pathAry[this.pathAry.length-1]);

		// スタートマーカー描画
		var icon = new GIcon(this.baseIcon);
		icon.image = "http://www.google.com/mapfiles/markerS.png";
		var marker = new GMarker(this.pathAry[0], icon);
		this.map.addOverlay(marker);
	}

	this.createEncodings();
}
RouteDelta.prototype.pathRedraw = RouteDelta_pathRedraw;


// GPolyline.fromEncoded 形式の経路情報を読む
function RouteDelta_gpfe2pathAry(gpfe)
{
	//gpfe = gpfe.replace(/%60/g, "`");
	gpfe = unescape(gpfe);
	var encodedPolyline = new GPolyline.fromEncoded({
		color: "#FF0000",
		weight: 10,
		points: gpfe,
		levels: "",
		zoomLevel: 32,
		numLevels: 4
	});			// gpfe以外のパラメータは、関係無いっす。GPolylineのインスタンスは後で作り直しちゃうんで。
	
	// 上で読んだ GPolyline を pathAry にぶち込む。
	this.pathAry = [];
	var len = encodedPolyline.getVertexCount();
	for ( i=0; i<len; i++ ) {
		this.pathAry.push(encodedPolyline.getVertex(i));
	}
	this.panIndex = this.pathAry.length;	// スクロール抑制
}
RouteDelta.prototype.gpfe2pathAry = RouteDelta_gpfe2pathAry;



function RouteDelta_startScroll()
{
	this.map.setCenter(new GLatLng(this.pathAry[0].lat(), this.pathAry[0].lng()));
	this.panIndex=1;
	this.panIndex2=0;
	//this.map_moveend();
}
RouteDelta.prototype.startScroll = RouteDelta_startScroll;

function RouteDelta_endScroll()
{
	this.panIndex = this.pathAry.length;
}
RouteDelta.prototype.endScroll = RouteDelta_endScroll;

function RouteDelta_intervalScroll()
{
	if ( this.panIndex < this.pathAry.length ) {
		var p0 = this.pathAry[this.panIndex-1];
		var p1 = this.pathAry[this.panIndex  ];
		if ( this.panIndex2 == 0 ) {
			this.pan_dlat = p1.lat()-p0.lat();
			this.pan_dlng = p1.lng()-p0.lng();

			var projection = this.map.getCurrentMapType().getProjection();
			var pp0 = projection.fromLatLngToPixel(this.pathAry[this.panIndex-1], this.map.getZoom());
			var pp1 = projection.fromLatLngToPixel(this.pathAry[this.panIndex  ], this.map.getZoom());
			var dx = pp1.x-pp0.x;
			var dy = pp1.y-pp0.y;
			this.panIndex2max = Math.sqrt(dx*dx+dy*dy);
			if ( this.panIndex2max<1 ) {
				this.panIndex2max=1;
			}
		}
		// 移動先の座標計算
		var lat = p0.lat()+this.pan_dlat/this.panIndex2max*this.panIndex2;
		var lng = p0.lng()+this.pan_dlng/this.panIndex2max*this.panIndex2;

		// 移動
		this.map.panTo(new GLatLng(lat,lng));
//alert(""+this.panIndex+","+this.panIndex2);

		this.panIndex2+=this.pan_dl;
		if ( this.panIndex2max <= this.panIndex2 ) {
			this.panIndex2 = 0;
			this.panIndex++;
		}
		else {
		}
	}
	else if ( this.panIndex2 == 0 ) {
		// キッチリ最後の点までスクロールする。
		this.map.panTo(this.pathAry[this.pathAry.length-1]);
		this.panIndex2+=this.pan_dl;
	}
}
RouteDelta.prototype.intervalScroll = RouteDelta_intervalScroll;

/* スクロールの継続を moveend イベントによりやろうとしたときの残骸
function RouteDelta_map_moveend()
{
	if ( this.panIndex < this.pathAry.length ) {
		this.myPanTo(this.pathAry[this.panIndex]);
		this.panIndex++;
	}
}
RouteDelta.prototype.map_moveend = RouteDelta_map_moveend;
*/

// GMap.panTo との違いは、
// 遠くの点でも瞬間移動せず、スクロールしてくれるところ。
function RouteDelta_myPanTo(LatLng)
{
	var projection = this.map.getCurrentMapType().getProjection();
	var p1 = projection.fromLatLngToPixel(this.map.getCenter(), this.map.getZoom());
	var p2 = projection.fromLatLngToPixel(LatLng              , this.map.getZoom());
	this.map.panBy(new GSize(p1.x-p2.x, p1.y-p2.y));
}
RouteDelta.prototype.myPanTo = RouteDelta_myPanTo;



// http://www.google.com/apis/maps/documentation/polylineutility.html
// よりコピペして変造

// Encode a signed number in the encode format.
function RouteDelta_encodeSignedNumber(num) {
  var sgn_num = num << 1;

  if (num < 0) {
    sgn_num = ~(sgn_num);
  }

  return(this.encodeNumber(sgn_num));
}
RouteDelta.prototype.encodeSignedNumber = RouteDelta_encodeSignedNumber;

// Encode an unsigned number in the encode format.
function RouteDelta_encodeNumber(num) {
  var encodeString = "";

  while (num >= 0x20) {
    encodeString += (String.fromCharCode((0x20 | (num & 0x1f)) + 63));
    num >>= 5;
  }

  encodeString += (String.fromCharCode(num + 63));
  return encodeString;
}
RouteDelta.prototype.encodeNumber = RouteDelta_encodeNumber;

// Create the encoded polyline and level strings.
function RouteDelta_createEncodings() {
	var i = 0;

	var plat = 0;
	var plng = 0;

	this.encoded_points = "";

	for(i = 0; i < this.pathAry.length; ++i) {
		var late5 = Math.floor(this.pathAry[i].lat() * 1e5);
		var lnge5 = Math.floor(this.pathAry[i].lng() * 1e5);
		this.encoded_points += this.encodeSignedNumber(late5 - plat) + this.encodeSignedNumber(lnge5 - plng);
		plat = late5;
		plng = lnge5;
	}
	this.zoom = this.map.getZoom();
	this.maptype = (this.map.getCurrentMapType()==G_SATELLITE_MAP) ? 1 : 0;
}
RouteDelta.prototype.createEncodings = RouteDelta_createEncodings;









// Google様のフォーマットがあるのに、独自フォーマットを作っちまったよ。
// 死亡。死蔵。使うな。

/*
  deltaフォーマット
    1文字目  バージョン
    2文字目  スケール (第5bitが 地図/衛星)
    それ以降 ⊿

⊿詳細
1文字目(pre)
543210
+      Lat符号(1:正 0:負)
 ++    Lat文字数(0-3)
   +   Lng符号(1:正 0:負)
    ++ Lng文字数(0-3)
2文字目以降 Lat Lng
*/

// this.pathAry から BASE64もどきpath
function RouteDelta_recalcDelta()
{
	// delta文字列を作る。このプログラムのキモ。
	this.deltaHtml = '';
	this.deltaText = '';

	// バージョン
	this.deltaHtml += '<font color="#666666">'+this.B64.charAt(2)+'</font>';
	this.deltaText += this.B64.charAt(2);

	// 現在のマップタイプとスケール
	var param2 = this.map.getZoom();
	if ( this.map.getCurrentMapType()==G_SATELLITE_MAP　) {
		// マップタイプが航空写真なら、上位ビットを立てる。
		param2 |= 32;
	}
	this.deltaHtml += '<font color="#666666">'+this.B64.charAt(param2)+'</font>';
	this.deltaText += this.B64.charAt(param2);

	for ( i=0; i<this.pathAry.length; i++ ) {
		// 前回との差分（最初の点の場合は(36,138)からの差分）
		d_lat = Math.round( (this.pathAry[i].lat() - (i ? this.pathAry[i-1].lat() :  36) )*this.resolution);
		d_lng = Math.round( (this.pathAry[i].lng() - (i ? this.pathAry[i-1].lng() : 138) )*this.resolution);
		//                0 1                22   2                      33      2 1        0

		this.bb64(d_lat);
		var l_lat = this.l;
		var s_lat = this.s;

		this.bb64(d_lng);
		l_lng = this.l;
		s_lng = this.s;

		// ⊿の1文字目を決定
		var pre=0;
		if ( l_lat > 0 ) {
			// 正
			pre += 32;	// 符号
			pre += (l_lat-1)*8;	// 文字数
		}
		else {
			// 符号はもともと0
			pre -= (l_lat+1)*8;	// 文字数
		}
		if ( l_lng > 0 ) {
			pre += 4	// 符号
			pre += l_lng-1;	// 文字数
		}
		else {
			// 符号はもともと0
			pre -= l_lng+1;	// 文字数
		}

		this.deltaHtml += '<font color="#0000cc">'+this.B64.charAt(pre)+'</font>' + '<font color="#00cc00">'+s_lat+'</font>' + '<font color="#cc0000">'+s_lng+'</font>';
		this.deltaText += this.B64.charAt(pre)+s_lat+s_lng;
	}//for
}
RouteDelta.prototype.recalcDelta = RouteDelta_recalcDelta;

// BASE64もどきpathから、this.pathAry を再生成
function RouteDelta_pathparam2pathAry(path)
{
	n_lat =  36;
	n_lng = 138;

	var version = this.B64.indexOf(path.charAt(0));
	if ( version <=0 ||  3 <= version) {
		alert('パラメータのバージョンが不正です。非対応と思われます。'+this.B64.indexOf(path.charAt(0)));
		return;
	}
	var scale = 1;
	if ( version == 1 ) {
		// バージョン１では精度がコンマ6桁だったんですよ。
		scale = 10;
	}

	var param2 = this.B64.indexOf(path.charAt(1));
	if ( param2 & 32 ) {
		// 上のビットが立っていたら、航空写真
		this.maptype = G_SATELLITE_MAP;
	}
	else {
		// 上のビットが立っていたら、地図
		this.maptype = G_NORMAL_MAP;
	}
	// 下5ビットはズーム値
	this.zoom = param2 & 31;

	i=2;
	while (i<path.length) {
		// 1文字目処理
		pre = this.B64.indexOf(path.charAt(i));
		l_lat = (pre&24)/8 +1;
		l_lng = (pre&3)    +1;
		if ( pre&32 ) {
			s_lat = +1;
		}
		else {
			s_lat = -1;
		}
		if ( pre&4 ) {
			s_lng = +1;
		}
		else {
			s_lng = -1;
		}
		i++;

		// lat処理
		d_lat = 0;
		for ( l=0;l<l_lat;l++ ) {
			if ( path.length <= i ) {
				alert("パラメータがが不正です。");
				this.pathAry = [];
				return;
			}
			d_lat *= 64;
			d_lat += this.B64.indexOf(path.charAt(i));
			i++;
		}
		d_lat *= s_lat;
		// lng処理
		d_lng = 0;
		for ( l=0;l<l_lng;l++ ) {
			if ( path.length <= i ) {
				alert("パラメータがが不正です。");
				this.pathAry = [];
				return;
			}
			d_lng *= 64;
			d_lng += this.B64.indexOf(path.charAt(i));
			i++;
		}
		d_lng *= s_lng;

		// 古いバージョン用の盲腸処理
		d_lat = d_lat/scale;
		d_lng = d_lng/scale;

		// 座標追加
		n_lat+=d_lat/this.resolution;
		n_lng+=d_lng/this.resolution;
		this.pathAry.push(new GLatLng(n_lat,n_lng));
	}

	this.panIndex = this.pathAry.length;	// スクロール抑制
}
RouteDelta.prototype.pathparam2pathAry = RouteDelta_pathparam2pathAry;

function RouteDelta_bb64(d)
{
	var sign = 1;	// 符号(1:正 0:負)
	if ( d<0 ) {
		d = -d;   // 反転
		sign=0; // 符号は負ですよ
	}

	// 64進数作成
	this.s = '';
	this.l = 0; // 長さ 1-4
	while ( d > 0 ) {
		c = d % 64;
		d -= c;
		d /= 64;
		this.s = this.B64.charAt(c) + this.s;
		this.l++;
	}
	if ( sign == 0 ) {
		this.l = -this.l;
	}
	if ( this.l==0 ) {
		this.l = 1;
		this.s = this.B64.charAt(0);
	}
}
RouteDelta.prototype.bb64 = RouteDelta_bb64;


