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()"/>&nbsp;&nbsp;&nbsp;<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 左上角經緯度:&nbsp;&nbsp;<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代码了,还是洗洗睡吧。

posted @ 2013-04-25 14:01  重庆Debug  阅读(1650)  评论(2编辑  收藏  举报