Google、Soso、Baidu卫星图url研究
前一段时间因为公司有个项目需要我研究一下Google的卫星图的地址规律,开始我以为会很艰难认为Google那么大公司url应该会加密什么的,结果发现Google和Soso、Baidu、Sogou这几个比较起来Google的url演算是最简单且明了的,废话不说了。
打开Google Map我们可以看到一张地图,将它放大或者缩小就可以看到不同的地图(很多张的图片)。url像这样:http://khm1.google.com/kh/v=125&src=app&x=0&y=0&z=0 (红色部分均为可变的参数)
Google的作法是世界的第0层(z=0)就是一张图构成:
z=1的效果就是将它切分为4份见下图:
依次类推,我们可以得到一个公式:NUM=2^(N+1)
NUM:x或者y方向上的图片数
N:第几层z轴
然后引入Mercator库(我自己写的麦卡拓转换)code:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Windows; 6 7 namespace MapsDownloader 8 { 9 class Mercator 10 { 11 private double NormalToMercator(double y) 12 { 13 y -= 0.5; 14 y *= 2.0 * Math.PI; 15 y = Math.Exp(y * 2.0); 16 y = (y - 1) / (y + 1.0); 17 y = Math.Asin(y); 18 y = y * -180.0 / Math.PI; 19 return y; 20 } 21 private double MercatorToNormal(double y) 22 { 23 y = -y * Math.PI / 180.0; 24 y = Math.Sin(y); 25 y = (1.0 + y) / (1.0 - y); 26 y = 0.5 * Math.Log(y); 27 y *= 1.0 / (2.0 * Math.PI); 28 y += 0.5; 29 return y; 30 } 31 private double getNormailByY(double y,int picnum) 32 { 33 return y/picnum; 34 } 35 private double getYByNormail(double y, int picnum) 36 { 37 return picnum * y; 38 } 39 /////////////////////////////////////////////// 40 ///传入:-26.851029008675013 返回:-26° 51' 3" 41 public string GetSexagesimalNotation(double x) 42 { 43 //to string format: 23° 27′ 30" 44 var ret = ""; 45 if (x < 0) 46 { 47 ret += '-'; 48 x = -x; 49 } 50 ret += Math.Floor(x); 51 ret += "° "; 52 53 x = (x - Math.Floor(x)) * 60; 54 ret += Math.Floor(x); 55 ret += "' "; 56 57 x = (x - Math.Floor(x)) * 60; 58 ret += Math.Floor(x); 59 ret += "\" "; 60 61 return ret; 62 } 63 public Point Google_XYZ_to_LatLng(int x, int y, int z) //lat [0] , lng [1] 64 { 65 Point LatLng = new Point(); 66 int picnum = 2 << (z - 1); //z层在x、y轴上存在多少张 67 double onex = 360.0 / picnum; //平均每一张占用多少经纬度 68 double xlat = onex * x; 69 if (xlat > 360.0) //如果超过360就剪掉它 70 xlat = xlat % 360.0; 71 if (xlat > 180.0) 72 LatLng.X = onex * x - 180.0; 73 else 74 LatLng.X = -(180.0 - onex * x); 75 LatLng.Y = NormalToMercator(getNormailByY(y, picnum)); 76 return LatLng; 77 } 78 public Point Google_LatLngZ_to_XY(double Lat, double Lng, int z) //return x,y array 79 { 80 Point xy = new Point(); 81 int picnum = 2 << (z - 1); //z层在x、y轴上存在多少张
82 double onex = 360.0 / picnum; ////平均每一张占用多少经纬度
83 Lat += 180.0;
84 xy.X = Convert.ToInt32(Lat / onex); //x
85 xy.Y = Convert.ToInt32(getYByNormail(MercatorToNormal(Lng), picnum)); //y
86 return xy;
87 }
88 }
89 }
主要是Y轴方向转换麻烦需要用到这些函数,因为Mercator当初设计的缺陷就是越往两极地区越不准确。
下面两个函数Google_XYZ_to_LatLng、Google_LatLngZ_to_XY提供了将XYZ转换成为 经纬度,和经纬度转换成Google的XYZ轴,有了这些基础我们就可以用html和JQuery写出一个简易的GoogleMap 网页键盘版本:
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 5 <title>Keyboard Google Map——By MaxHo</title> 6 <script type="text/javascript" src="jquery-1.8.0.min.js"></script> 7 <script type="text/javascript"> 8 $(document).ready(function() 9 { 10 readImage(); 11 }); 12 function readImage() 13 { 14 var url=""; 15 //http://khmdbs0.google.com/pm?v=9&src=app&x=0&y=4&z=4&s=Gali 16 //http://khm1.google.com/kh/v=125&src=app&x= 17 // 18 19 20 if($("#mapTypeSelect").val() == 1) 21 { 22 var xx = parseInt($("#x").val()); 23 var yy = parseInt($("#y").val()); 24 var zz = parseInt($("#z").val()); 25 var picnum = 2<<(zz-1); 26 var onex = 360 / picnum; 27 /*var oney = 180 / picnum; 28 if(oney * yy > 90) 29 var lng = -(oney * yy - 90); 30 else 31 var lng = 90 - oney * yy;*/ 32 var lng = NormalToMercator (getNormailByY(yy,picnum)); 33 if(onex * xx > 180) 34 var lat = onex * xx - 180; 35 else 36 var lat = -(180 - onex * xx); 37 $("#latlng").text(lng + " , " + lat); 38 $("table td").each(function(i){ 39 var ix = i%5 + parseInt($("#x").val()); 40 var iy = parseInt(i/5) + parseInt($("#y").val()); 41 url = "http://khm1.google.com/kh/v=125&src=app&x="+ix 42 +"&y="+iy+"&z="+$("#z").val(); 43 $(this).html("<img style='width:100%;height:100%;' src='"+url+"'/>"); 44 }); 45 } 46 else if($("#mapTypeSelect").val() == 2) //soso 47 { 48 var xx = parseInt($("#x").val()); 49 var yy = parseInt($("#y").val()); 50 var zz = parseInt($("#z").val()); 51 $("table td").each(function(i){ 52 var ix = i%5 + parseInt($("#x").val()); 53 var iy = 2 - parseInt(i/5) + parseInt($("#y").val()); 54 var dx = Math.floor(ix/16); 55 var dy = Math.floor(iy/16); 56 url = "http://p1.map.soso.com/sateTiles/"+$("#z").val() 57 +"/"+dx+"/"+dy+"/"+ix+"_"+iy+".jpg"; 58 $(this).html("<img style='width:100%;height:100%;' src='"+url+"'/>"); 59 }); 60 } 61 else if($("#mapTypeSelect").val() == 3) //baidu 62 { 63 var xx = parseInt($("#x").val()); 64 var yy = parseInt($("#y").val()); 65 var zz = parseInt($("#z").val()); 66 $("table td").each(function(i){ 67 var ix = i%5 + parseInt($("#x").val()); 68 var iy = 2 - parseInt(i/5) + parseInt($("#y").val()); 69 url = "http://q1.baidu.com/it/u=x="+ix+";y="+iy+";z="+$("#z").val() 70 +";v=009;type=sate&fm=46"; 71 $(this).html("<img style='width:100%;height:100%;' src='"+url+"'/>"); 72 }); 73 } 74 else if($("#mapTypeSelect").val() == 4) //cangbao 75 { 76 var xx = parseInt($("#x").val()); 77 var yy = parseInt($("#y").val()); 78 var zz = parseInt($("#z").val()); 79 var picnum = 2<<(zz-1); 80 var onex = 360 / picnum; 81 /*var oney = 180 / picnum; 82 if(oney * yy > 90) 83 var lng = -(oney * yy - 90); 84 else 85 var lng = 90 - oney * yy;*/ 86 var lng = NormalToMercator (getNormailByY(yy,picnum)); 87 if(onex * xx > 180) 88 var lat = onex * xx - 180; 89 else 90 var lat = -(180 - onex * xx); 91 $("#latlng").text(lng + " , " + lat); 92 $("table td").each(function(i){ 93 var ix = i%5 + parseInt($("#x").val()); 94 var iy = parseInt(i/5) + parseInt($("#y").val()); 95 url = "http://khmdbs0.google.com/pm?v=9&src=app&x="+ix 96 +"&y="+iy+"&z="+$("#z").val(); 97 $(this).html("<img style='width:100%;height:100%;' src='"+url+"'/>"); 98 }); 99 } 100 } 101 function left() 102 { 103 var xx = parseInt($("#x").val()); 104 $("#x").val(--xx); 105 } 106 function up() 107 { 108 var yy = parseInt($("#y").val()); 109 if($("#mapTypeSelect").val() == 2 || $("#mapTypeSelect").val() == 3 ) 110 $("#y").val(++yy); 111 else 112 $("#y").val(--yy); 113 } 114 function right() 115 { 116 var xx = parseInt($("#x").val()); 117 $("#x").val(++xx); 118 } 119 function down() 120 { 121 var yy = parseInt($("#y").val()); 122 if($("#mapTypeSelect").val() == 2 || $("#mapTypeSelect").val() == 3 ) 123 $("#y").val(--yy); 124 else 125 $("#y").val(++yy); 126 } 127 function enter() 128 { 129 var xx = parseInt($("#x").val()); 130 var yy = parseInt($("#y").val()); 131 var zz = parseInt($("#z").val()); 132 if($("#mapTypeSelect").val() == 1) 133 { 134 var centerX = (xx+2)*2 - 2; 135 var centerY = (yy+1)*2 - 1; 136 var centerZ = zz+1; 137 } 138 else if($("#mapTypeSelect").val() == 2 || $("#mapTypeSelect").val() == 3 ) 139 { 140 var centerX = (xx+2)*2 - 2; 141 var centerY = (yy-1)*2 + 3; 142 var centerZ = zz+1; 143 } 144 $("#x").val(centerX); 145 $("#y").val(centerY); 146 $("#z").val(centerZ); 147 } 148 function exit() 149 { 150 var xx = parseInt($("#x").val()); 151 var yy = parseInt($("#y").val()); 152 var zz = parseInt($("#z").val()); 153 if($("#mapTypeSelect").val() == 1) 154 { 155 var centerX = parseInt((xx+2)/2) - 2; 156 var centerY = parseInt((yy+1)/2) - 1; 157 var centerZ = zz-1; 158 } 159 else if($("#mapTypeSelect").val() == 2 || $("#mapTypeSelect").val() == 3 ) 160 { 161 var centerX = parseInt((xx+2)/2) - 2; 162 var centerY = parseInt((yy-1)/2); 163 var centerZ = zz-1; 164 } 165 $("#x").val(centerX); 166 $("#y").val(centerY); 167 $("#z").val(centerZ); 168 } 169 $(this).bind('focus',function(event){ 170 $(this).select(); 171 }); 172 $(document).bind('keydown',function(e){ 173 e = (e) ? e : ((window.event) ? window.event : ""); 174 var key = e.keyCode?e.keyCode:e.which; 175 switch(key) { 176 case 37: 177 //left 178 left(); 179 readImage(); 180 break; 181 case 38: 182 //up 183 up(); 184 readImage(); 185 break; 186 case 39: 187 //right 188 right(); 189 readImage(); 190 break; 191 case 40: 192 //down 193 down(); 194 readImage(); 195 break; 196 /*case 13: 197 //enter 198 enter(); 199 readImage(); 200 break;*/ 201 case 69: 202 //E 203 enter(); 204 readImage(); 205 break; 206 /*case 27: 207 //exit 208 exit(); 209 readImage(); 210 break;*/ 211 case 81: 212 //Q 213 exit(); 214 readImage(); 215 break; 216 default: 217 //alert(key); 218 break; 219 } 220 }); 221 function MercatorToNormal(y) 222 { 223 y = -y * Math.PI / 180; // convert to radians 224 y = Math.sin(y); 225 y = (1+y)/(1-y); 226 y = 0.5 * Math.log(y); 227 y *= 1.0 / (2 * Math.PI); // scale factor from radians to normalized 228 y += 0.5; // and make y range from 0 - 1 229 return y; 230 } 231 232 function NormalToMercator(y) 233 { 234 y -= 0.5; 235 y *= 2 * Math.PI; 236 y = Math.exp(y * 2); 237 y = (y-1)/(y+1); 238 y = Math.asin(y); 239 y = y * -180/Math.PI; 240 return y; 241 } 242 function getNormailByY(y,picnum) 243 { 244 /*var scale = 1.0; 245 var ry = 0.0; 246 for (var i = 0; i< y ; i++) 247 { 248 scale *= 0.5; 249 ry += scale; 250 } 251 return ry;*/ 252 return y/picnum; 253 } 254 function changeMapTypeSelect() 255 { 256 if($("#mapTypeSelect").val() == 2) 257 { 258 $("#x").val(10); 259 $("#y").val(8); 260 $("#z").val(4); 261 } 262 else if($("#mapTypeSelect").val() == 1) 263 { 264 $("#x").val(10); 265 $("#y").val(5); 266 $("#z").val(4); 267 } 268 else if ($("#mapTypeSelect").val() == 3 ) 269 { 270 $("#x").val(3); 271 $("#y").val(0); 272 $("#z").val(5); 273 } 274 readImage(); 275 } 276 277 </script> 278 </head> 279 <body> 280 <form action="#"> 281 x:<input id="x" type="text" value="10" style="width:60px;"/> 282 y:<input id="y" type="text" value="5" style="width:60px;"/> 283 z:<input id="z" type="text" value="4" style="width:60px;"/> 284 <input type="submit" value="重新載入" onclick="readImage()"/> <span style="font-size:12px; color:#808080"> 285 <select id="mapTypeSelect" onchange="changeMapTypeSelect()"> 286 <option value="1" selected="selected">GoogleMap</option> 287 <option value="2">SosoMap</option> 288 <option value="3">BaiduMap</option> 289 <option value="4">藏宝图</option> 290 </select> 291 使用鍵盤熱鍵上下左右鍵調節方向,E、Q控制地圖縮放</span> 292 <input type="button" value="←" onclick="left(),readImage()"/> 293 <input type="button" value="↑" onclick="up(),readImage()"/> 294 <input type="button" value="↓" onclick="down(),readImage()"/> 295 <input type="button" value="→" onclick="right(),readImage()"/> 296 <input type="button" value="㈩" onclick="enter(),readImage()"/> 297 <input type="button" value="㈠" onclick="exit(),readImage()"/> 298 </form> 299 左上角經緯度: <span id="latlng"></span> 300 <table border="0" cellspacing="0" cellpadding="0"> 301 <tr> 302 <td></td> 303 <td></td> 304 <td></td> 305 <td></td> 306 <td></td> 307 </tr> 308 <tr> 309 <td></td> 310 <td></td> 311 <td></td> 312 <td></td> 313 <td></td> 314 </tr> 315 <tr> 316 <td></td> 317 <td></td> 318 <td></td> 319 <td></td> 320 <td></td> 321 </tr> 322 </table> 323 </body> 324 </html>
soso map和baidu map的地图url规律就蛋疼许多,因为他们的Y轴非要与google的方向相反。。没法,为了适应他们单独写了函数
这还不算什么,soso还单独弄出来一个参数dx、dy,后来我还是自己看sosomap中的已经被搅乱的js代码才分析出来只是由x和y通过Math.floor(x/16)运算得到的,腾讯这样搞这有木有意义@@ ,为了后台省资源?
百度也是跟着腾讯一样把地图的Y轴弄反,不过没有dx、dy,不过他们我实测都有经过偏移模组的偏移。也就是说你给定一个经纬度通过标准的麦卡拓算出来的xyz总有一点点几百米的偏移。这些倒是怪不到他。更变态的是sogoumap完全没有办法猜清楚是什么规律,我直接选择放弃了,都不想看它的js代码了,还是洗洗睡吧。