一種Python爬取百度地圖瓦片的方式

  • 2019 年 10 月 8 日
  • 筆記

瓦片是互聯網地圖webGIS組織地圖數據的一種方式,最近的一個項目需要獲取一定區域內的百度地圖瓦片;ArcGIS的一個插件ArcBruTile支援很多地圖源(如OSM、Bing)的瓦片獲取,但是沒有百度地圖的,Github上的一些項目(如pyMap)也不支援百度瓦片的下載,

因此打算自己去獲取。開始是想用百度API的靜態圖服務,但是我只有一個區域的邊界矩形(MBR),而它接受的參數是中心點坐標和zoom的尺度,其請求URL的核心參數為center=116.403,39.914&width=1024&height=1024&zoom=11,要分塊下載很麻煩,想了很多辦法去算坐標和進行坐標距離換算,走了一些彎路,後來發現百度地圖有一個隱晦的支援參數為x,y,z的調用,而x,y的變化是比較有規律的,因此只需要有邊界的x和y再疊加就行。我是下載區域內的小圖片再拼接為大圖使用的,最後有上萬張小圖片,合併為一個200多MB的大圖。

下載下來的瓦片

具體實現過程如下:

1,獲取圖片

百度坐標拾取系統(可以用關鍵詞搜索得到網址)網頁,先按F12調出控制台,用坐標反查定位到左下坐標,並且調好層級,我要用的是17級的地圖,然後找到一張圖片手動確定x1和y1;(雖然有邏輯可以根據坐標和層級算x和y,但百度地圖版的實現起來還是費些時間的,我沒找到現成的程式碼,如果讀者有發現或寫過實現這個功能的程式碼歡迎在評論里告知,非常感謝),再定位到邊界的右上角,同樣確定好x2,y2,然後用requests庫寫獲取圖片的程式碼,比較建議先定y,改x,我用先循環y的方式保存的圖片合併起來更複雜些,循環下去;可以得到整個區域的圖片;

百度坐標拾取系統採用瓦片底圖

(先驗知識:百度地圖的瓦片是從左下角算的,而不是Google Map的左上角開始;)

import requests    def getTileByXYZ(): #根據x,y,z參數獲取瓦片     z=17     xidx=[22568,22676]     yidx=[6898,7008]      for y in range(yidx[0],yidx[1]+1):          for x in range(xidx[0],xidx[1]+1):             url="http://online3.map.bdimg.com/tile/?qt=tile&x={x}&y={y}&z=17&styles=pl"                   "&scaler=1&udt=20180601".format(x=x,y=y)             savePngByXYZ(url, x, y, z)         print(y)    def savePngByXYZ(url,x,y,z=17): #保存圖片     r = requests.get(url)     sname="./cdZoomImg/cd_{x}_{y}.png".format(x=x,y=y) #這裡建議保存編碼是y_x 這樣下面合併圖片也要適當改程式碼     with open(sname, 'ab') as pngf:          for chunk in r.iter_content(chunk_size=1024):              if chunk:                 pngf.write(chunk)                 pngf.flush()

2,合併瓦片

一共爬了1萬多張瓦片,花了40多分鐘(這個要看電腦性能),每張瓦片是256*256像素的方形區域;接下來就是合併的過程了,本來打算一次性合併完畢,但是弄得電腦記憶體不足了,於是先合併x相同的圖片到另一個文件夾,形成一個個長條形的圖片,每張尺寸變成了256*28672像素,再跑一遍把這些圖片合併到一起,就形成了一張27648*28672的地圖圖片,可以用來作為ArcGIS一些空間分析的底圖。

逐步合併瓦片

import os  import glob  from PIL import Image  def complieImg():     #命名規則:cd_x_y.png 左下坐標系     #同一個x 同1列,y增加,圖片在上面     #假設輸入排好序了     p = "./chengduImg"     plst = glob.glob(os.path.join(p, '*.png'))   xmin=((plst[0].split("\")[1]).split(".")[0]).split('_')[1]     alst=[] #3維     qlst=[]      for f in plst:         w=((f.split("\")[1]).split(".")[0]).split('_') #['cd', '22568', '6898']         w[0] = f          if w[1]==xmin:             qlst.append(w.copy())          else:             alst.append(qlst.copy())             xmin=w[1]             qlst=[]     m2 = [256*len(alst[0]), 256 * len(alst)]  #im2=Image.new('RGBA', (m2[0], m2[1]))     print(m2)     psave = "./complexLevel"     iw=0     for k in alst:#k裡面裝的是x相同的值,y應該遞增         plen=len(k)         msize = [256, 256 * (plen+1)]         print(msize)         toImage = Image.new('RGBA', (msize[0], msize[1]))          for i in range(plen):             fromImage = Image.open(k[plen - i - 1][0])             toImage.paste(fromImage, (0 * msize[0], i * msize[0]))       sname="/m_{x}.png".format(x=k[0][1])         iw+=1           toImage.save(psave+sname)    def complieImgInY():     #合併長條形圖片,x變化,y不變 長圖是complieImg()里生成的     p = "./complexLevel" #chengduImg     plst = glob.glob(os.path.join(p, '*.png'))   xmin=((plst[0].split("\")[1]).split(".")[0]).split('_')[1]     ima21=Image.open(plst[0])     w=np.array(ima21).shape     print(w)     psave = "D:/wexcel"     plen=len(plst)     msize = [w[1]*plen/2, w[0]/2]     print(msize)     toImage = Image.new('RGBA', (int(msize[0]), int(msize[1])))      for i in range(plen):         fromImage = Image.open(plst[i])         fromImage=fromImage.resize((int(256/2),int(msize[1])), Image.ANTIALIAS)         toImage.paste(fromImage, (int(i * 256/2), 0))   sname="/chengduMap.png"   toImage.save(psave+sname) #保存圖片

最後得到一張200多MB的整合圖片。最後程式碼更新於https://github.com/QLWeilcf/LcfGeoProject/tree/master/WebGISLyn。

OutputImg