計算機技術

Google、搜搜、百度三家衛星地圖URL研究

前一段時間因為公司有個項目需要我研究一下 Google 的衛星圖的地址規律,開始我以為 會很艱難認為Google那麼大公司 URL 應該會加密什麼的,結果發現 Google 和搜搜百度搜狗 這幾個比較起來Google的 url演算是最簡單且明了的,廢話不說了。

打開Google Map我們可以看到一張地圖,將它放大或者縮小就可以看到不同的地圖(很多張的圖片)。url像這樣:http://khm1.google.com/kh/v=125&src=app&x=0&y=0&z=0   (紅色部分均為可變的參數)

Google 的作法是世界的第0層(z=0)就是一張圖構成:

v=125&src=app&x=-2&y=0&z=0

z=1的效果就是將它切分為4份見下圖:

 v=125&src=app&x=0&y=0&z=1  v=125&src=app&x=1&y=0&z=1
 v=125&src=app&x=0&y=1&z=1  v=125&src=app&x=1&y=1&z=1

依次類推,我們可以得到一個公式:NUM=2^(N+1)

NUM:x或者y方向上的圖片數

N:第幾層z軸

然後引入Mercator庫(我自己寫的麥卡拓轉換)code:

[sourcecode language="csharp"]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;

namespace MapsDownloader
{
class Mercator
{
private double NormalToMercator(double y)
{
y -= 0.5;
y *= 2.0 * Math.PI;
y = Math.Exp(y * 2.0);
y = (y - 1) / (y + 1.0);
y = Math.Asin(y);
y = y * -180.0 / Math.PI;
return y;
}
private double MercatorToNormal(double y)
{
y = -y * Math.PI / 180.0;
y = Math.Sin(y);
y = (1.0 + y) / (1.0 - y);
y = 0.5 * Math.Log(y);
y *= 1.0 / (2.0 * Math.PI);
y += 0.5;
return y;
}
private double getNormailByY(double y,int picnum)
{
return y/picnum;
}
private double getYByNormail(double y, int picnum)
{
return picnum * y;
}
///////////////////////////////////////////////
///傳入:-26.851029008675013 返回:-26° 51' 3"
public string GetSexagesimalNotation(double x)
{
//to string format: 23° 27′ 30"
var ret = "";
if (x < 0)
{
ret += '-';
x = -x;
}
ret += Math.Floor(x);
ret += "° ";

x = (x - Math.Floor(x)) * 60;
ret += Math.Floor(x);
ret += "' ";

x = (x - Math.Floor(x)) * 60;
ret += Math.Floor(x);
ret += "" ";

return ret;
}
public Point Google_XYZ_to_LatLng(int x, int y, int z) //lat [0] , lng [1]
{
Point LatLng = new Point();
int picnum = 2 << (z - 1); //z層在x、y軸上存在多少張 double onex = 360.0 / picnum; //平均每一張佔用多少經緯度 double xlat = onex * x; if (xlat > 360.0) //如果超過360就剪掉它
xlat = xlat % 360.0;
if (xlat > 180.0)
LatLng.X = onex * x - 180.0;
else
LatLng.X = -(180.0 - onex * x);
LatLng.Y = NormalToMercator(getNormailByY(y, picnum));
return LatLng;
}
public Point Google_LatLngZ_to_XY(double Lat, double Lng, int z) //return x,y array
{
Point xy = new Point();
int picnum = 2 << (z - 1); //z層在x、y軸上存在多少張
double onex = 360.0 / picnum; ////平均每一張佔用多少經緯度
Lat += 180.0;
xy.X = Convert.ToInt32(Lat / onex); //x
xy.Y = Convert.ToInt32(getYByNormail(MercatorToNormal(Lng), picnum)); //y
return xy;
}
}
}
[/sourcecode]

主要是Y軸方向轉換麻煩需要用到這些函數,因為Mercator當初設計的缺陷就是越往兩極地區越不準確。

下面兩個函數Google_XYZ_to_LatLng、Google_LatLngZ_to_XY提供了將XYZ轉換成為 經緯度,和經緯度轉換成Google的XYZ軸,有了這些基礎我們就可以用html和JQuery寫出一個簡易的GoogleMap 網頁鍵盤版本:

[sourcecode language="html"]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Keyboard Google Map——By MaxHo</title>
<script type="text/javascript" src="jquery-1.8.0.min.js"></script>
<script type="text/javascript">
$(document).ready(function()
{
readImage();
});
function readImage()
{
var url="";
//http://khmdbs0.google.com/pm?v=9&src=app&x=0&y=4&z=4&s=Gali
//http://khm1.google.com/kh/v=125&src=app&x=
//

if($("#mapTypeSelect").val() == 1)
{
var xx = parseInt($("#x").val());
var yy = parseInt($("#y").val());
var zz = parseInt($("#z").val());
var picnum = 2<<(zz-1);
var onex = 360 / picnum;
/*var oney = 180 / picnum;
if(oney * yy > 90)
var lng = -(oney * yy - 90);
else
var lng = 90 - oney * yy;*/
var lng = NormalToMercator (getNormailByY(yy,picnum));
if(onex * xx > 180)
var lat = onex * xx - 180;
else
var lat = -(180 - onex * xx);
$("#latlng").text(lng + " , " + lat);
$("table td").each(function(i){
var ix = i%5 + parseInt($("#x").val());
var iy = parseInt(i/5) + parseInt($("#y").val());
url = "http://khm1.google.com/kh/v=125&src=app&x="+ix
+"&y="+iy+"&z="+$("#z").val();
$(this).html("<img style='width:100%;height:100%;' src='"+url+"'/>");
});
}
else if($("#mapTypeSelect").val() == 2)        //soso
{
var xx = parseInt($("#x").val());
var yy = parseInt($("#y").val());
var zz = parseInt($("#z").val());
$("table td").each(function(i){
var ix = i%5 + parseInt($("#x").val());
var iy = 2 - parseInt(i/5) + parseInt($("#y").val());
var dx = Math.floor(ix/16);
var dy = Math.floor(iy/16);
url = "http://p1.map.soso.com/sateTiles/"+$("#z").val()
+"/"+dx+"/"+dy+"/"+ix+"_"+iy+".jpg";
$(this).html("<img style='width:100%;height:100%;' src='"+url+"'/>");
});
}
else if($("#mapTypeSelect").val() == 3)        //baidu
{
var xx = parseInt($("#x").val());
var yy = parseInt($("#y").val());
var zz = parseInt($("#z").val());
$("table td").each(function(i){
var ix = i%5 + parseInt($("#x").val());
var iy = 2 - parseInt(i/5) + parseInt($("#y").val());
url = "http://q1.baidu.com/it/u=x="+ix+";y="+iy+";z="+$("#z").val()
+";v=009;type=sate&fm=46";
$(this).html("<img style='width:100%;height:100%;' src='"+url+"'/>");
});
}
else if($("#mapTypeSelect").val() == 4)        //cangbao
{
var xx = parseInt($("#x").val());
var yy = parseInt($("#y").val());
var zz = parseInt($("#z").val());
var picnum = 2<<(zz-1);
var onex = 360 / picnum;
/*var oney = 180 / picnum;
if(oney * yy > 90)
var lng = -(oney * yy - 90);
else
var lng = 90 - oney * yy;*/
var lng = NormalToMercator (getNormailByY(yy,picnum));
if(onex * xx > 180)
var lat = onex * xx - 180;
else
var lat = -(180 - onex * xx);
$("#latlng").text(lng + " , " + lat);
$("table td").each(function(i){
var ix = i%5 + parseInt($("#x").val());
var iy = parseInt(i/5) + parseInt($("#y").val());
url = "http://khmdbs0.google.com/pm?v=9&src=app&x="+ix
+"&y="+iy+"&z="+$("#z").val();
$(this).html("<img style='width:100%;height:100%;' src='"+url+"'/>");
});
}
}
function left()
{
var xx = parseInt($("#x").val());
$("#x").val(--xx);
}
function up()
{
var yy = parseInt($("#y").val());
if($("#mapTypeSelect").val() == 2 || $("#mapTypeSelect").val() == 3 )
$("#y").val(++yy);
else
$("#y").val(--yy);
}
function right()
{
var xx = parseInt($("#x").val());
$("#x").val(++xx);
}
function down()
{
var yy = parseInt($("#y").val());
if($("#mapTypeSelect").val() == 2 || $("#mapTypeSelect").val() == 3 )
$("#y").val(--yy);
else
$("#y").val(++yy);
}
function enter()
{
var xx = parseInt($("#x").val());
var yy = parseInt($("#y").val());
var zz = parseInt($("#z").val());
if($("#mapTypeSelect").val() == 1)
{
var centerX = (xx+2)*2 - 2;
var centerY = (yy+1)*2 - 1;
var centerZ = zz+1;
}
else if($("#mapTypeSelect").val() == 2 || $("#mapTypeSelect").val() == 3 )
{
var centerX = (xx+2)*2 - 2;
var centerY = (yy-1)*2 + 3;
var centerZ = zz+1;
}
$("#x").val(centerX);
$("#y").val(centerY);
$("#z").val(centerZ);
}
function exit()
{
var xx = parseInt($("#x").val());
var yy = parseInt($("#y").val());
var zz = parseInt($("#z").val());
if($("#mapTypeSelect").val() == 1)
{
var centerX = parseInt((xx+2)/2) - 2;
var centerY = parseInt((yy+1)/2) - 1;
var centerZ = zz-1;
}
else if($("#mapTypeSelect").val() == 2 || $("#mapTypeSelect").val() == 3 )
{
var centerX = parseInt((xx+2)/2) - 2;
var centerY = parseInt((yy-1)/2);
var centerZ = zz-1;
}
$("#x").val(centerX);
$("#y").val(centerY);
$("#z").val(centerZ);
}
$(this).bind('focus',function(event){
$(this).select();
});
$(document).bind('keydown',function(e){
e = (e) ? e : ((window.event) ? window.event : "");
var key = e.keyCode?e.keyCode:e.which;
switch(key) {
case 37:
//left
left();
readImage();
break;
case 38:
//up
up();
readImage();
break;
case 39:
//right
right();
readImage();
break;
case 40:
//down
down();
readImage();
break;
/*case 13:
//enter
enter();
readImage();
break;*/
case 69:
//E
enter();
readImage();
break;
/*case 27:
//exit
exit();
readImage();
break;*/
case 81:
//Q
exit();
readImage();
break;
default:
//alert(key);
break;
}
});
function MercatorToNormal(y)
{
y = -y * Math.PI / 180;    // convert to radians
y = Math.sin(y);
y = (1+y)/(1-y);
y = 0.5 * Math.log(y);
y *= 1.0 / (2 * Math.PI);    // scale factor from radians to normalized
y += 0.5;    // and make y range from 0 - 1
return y;
}

function NormalToMercator(y)
{
y -= 0.5;
y *= 2 * Math.PI;
y = Math.exp(y * 2);
y = (y-1)/(y+1);
y = Math.asin(y);
y = y * -180/Math.PI;
return y;
}
function getNormailByY(y,picnum)
{
/*var scale = 1.0;
var ry = 0.0;
for (var i = 0; i< y ; i++)
{
scale *= 0.5;
ry += scale;
}
return ry;*/
return y/picnum;
}
function changeMapTypeSelect()
{
if($("#mapTypeSelect").val() == 2)
{
$("#x").val(10);
$("#y").val(8);
$("#z").val(4);
}
else if($("#mapTypeSelect").val() == 1)
{
$("#x").val(10);
$("#y").val(5);
$("#z").val(4);
}
else if ($("#mapTypeSelect").val() == 3 )
{
$("#x").val(3);
$("#y").val(0);
$("#z").val(5);
}
readImage();
}

</script>
</head>
<body>
<form action="#">
x:<input id="x" type="text" value="10" style="width:60px;"/>
y:<input id="y" type="text" value="5" style="width:60px;"/>
z:<input id="z" type="text" value="4" style="width:60px;"/>
<input type="submit" value="重新載入" onclick="readImage()"/>&nbsp;&nbsp;&nbsp;<span style="font-size:12px; color:#808080">
<select id="mapTypeSelect" onchange="changeMapTypeSelect()">
<option value="1" selected="selected">GoogleMap</option>
<option value="2">SosoMap</option>
<option value="3">BaiduMap</option>
<option value="4">藏寶圖</option>
</select>
使用鍵盤熱鍵上下左右鍵調節方向,E、Q控制地圖縮放</span>
<input type="button" value="←" onclick="left(),readImage()"/>
<input type="button" value="↑" onclick="up(),readImage()"/>
<input type="button" value="↓" onclick="down(),readImage()"/>
<input type="button" value="→" onclick="right(),readImage()"/>
<input type="button" value="㈩" onclick="enter(),readImage()"/>
<input type="button" value="㈠" onclick="exit(),readImage()"/>
</form>
左上角經緯度:&nbsp;&nbsp;<span id="latlng"></span>
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</body>
</html>
[/sourcecode]

soso map和baidu map的地圖url規律就蛋疼許多,因為他們的Y軸非要與google的方向相反。。沒法,為了適應他們單獨寫了函數

這還不算什麼,soso還單獨弄出來一個參數dx、dy,後來我還是自己看sosomap中的已經被攪亂的js代碼才分析出來只是由x和y通過Math.floor(x/16)運算得到的,騰訊這樣搞這有木有意義@@ ,為了後台省資源?

百度也是跟著騰訊一樣把地圖的Y軸弄反,不過沒有dx、dy,不過他們我實測都有經過偏移模組的偏移。也就是說你給定一個經緯度通過標準的麥卡拓算 出來的xyz總有一點點幾百米的偏移。這些倒是怪不到他。更變態的是sogoumap完全沒有辦法猜清楚是什麼規律,我直接選擇放棄了,都不想看它的js 代碼了,還是洗洗睡吧。

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0

You may also like

Leave a reply

您的電子郵箱地址不會被公開。 必填項已用 * 標註

此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

計算機技術

Firebug 與 DevTools 的集成

你可能已經聽說過我們對統一 Firefox 的本地開發人員工具(DevTools)和 Firebug 的努力。我們一直在努力地將的所有最喜歡的 Firebug 功能添加到本地 DevTools 中,使 […]
計算機技術

判斷Linux伺服器架構是32位/64位

作為一個 Unix 系統的新手用戶,我可以怎麼判斷我的 Unix 伺服器安裝的是32位或者64位的操作系統呢?你可以使用如下的命令來獲取關於 Unix 內核和 CPU 架構的信息。