Python獲取 bing 地圖發佈本身的 TMS 服務(二)解決海量瓦片存取問題

金字塔結構的瓦片數量有多大

以目前互聯網經常使用的WebMecator爲例python

  • 第一層:4幅256*256影像瓦片(JPG或PNG等)
  • 第二層:42
  • 第三層:43
  • 依次類推
    好比計算第1層至第18層的瓦片總數目(等比數列求和)91625968980個,大約916億。存儲空間估算在近百T。

瓦片直接存儲在文件系統中的缺點

  • 文件系統對文件數量、大小的限制
  • 不易遷移、備份
  • 等等

解決方案

這個問題本質上是對海量小數據的管理,不少互聯網大廠都有比較成熟的方案,只須要根據具體狀況進行選擇調整便可。sql

單機存儲

採用sqlite
存儲在多個sqlite中,sqlite文件名保證了惟一性,與(row,column, level)一一對應。json

  • (row,column, level)能夠轉爲惟一數字,好比QuadKey,或者其餘編碼方式
  • sqlite移動與管理就比較方便。

注意sqlite單文件的大小不要太大。服務器

集羣存儲

使用HDFS等網絡化存儲方案。網絡

一個試驗

# -*- coding: utf-8 -*-
"""下載區域影像
從第一層到指定層
 
多線程版
 
存儲到sqlite中
 
"""
 
import requests
# python3的thread模塊
import _thread
import random
import time
from random import random
import os.path
import QuadKey.quadkey as quadkey
import shutil
import secrets as secrets
 
import sqlite_util as dbutil
 
 
# 下載的最細層
tileZoom = 10
rootTileDir = "tiles_db"
 
# 分的db數量,採用質數
 
db_num = 1511
lat_min = -90
lat_max = 90
lon_min = -180
lon_max = 180
# MS doesn't want you hardcoding the URLs to the tile server. This request asks for the Aerial
# url template. Replace {quadkey}
response = requests.get("https://dev.virtualearth.net/REST/V1/Imagery/Metadata/Aerial?key=%s" % (secrets.bingKey))
 
# 返回結果
data = response.json()
print(data)
 
# grabs the data we need from the response.
# 例如:http://ecn.{subdomain}.tiles.virtualearth.net/tiles/a{quadkey}.jpeg?g=7786
tileUrlTemplate = data['resourceSets'][0]['resources'][0]['imageUrl']
# 例如:['t0', 't1', 't2', 't3']
imageDomains = data['resourceSets'][0]['resources'][0]['imageUrlSubdomains']
 
if (os.path.exists(rootTileDir) == False):
os.mkdir(rootTileDir)
 
bingTilesDir = os.path.join(rootTileDir, "bing")
 
if (os.path.exists(bingTilesDir) == False):
os.mkdir(bingTilesDir)
 
 
 
def get_tiles_by_pixel(tilePixel):
"""
下載該點之上的瓦片
 
:param lat:
:param lon:
:return:
"""
 
"""get pixel coordinates"""
# tilePixel = quadkey.TileSystem.geo_to_pixel((lat, lon), tileZoom)
 
# print(tilePixel)
 
pixel = tilePixel
geo = quadkey.TileSystem.pixel_to_geo(pixel, tileZoom)
# 計算四鍵
qk = quadkey.from_geo(geo, tileZoom)
 
# 四鍵
qkStr = str(qk)
 
 
#
qkArray = []
for index in range(tileZoom):
qkArray.append(qkStr[0:index + 1])
 
print(qkArray)
# 存放路徑
for qk in qkArray:
# db位置
dbPath = "%s/%s.db" % (bingTilesDir, int(qk) % db_num )
print(dbPath)
 
if (os.path.exists(dbPath) == False):
# os.mkdir(dbPath)
dbutil.create_db(dbPath)
 
 
 
# 下載影像
 
if (dbutil.is_exists(dbPath, qk)):
# already downloaded
dbutil.save_images(dbPath, qk)
ok = 1
else:
print("下載中", end='')
 
url = tileUrlTemplate.replace("{subdomain}", imageDomains[0])
url = url.replace("{quadkey}", qk)
url = "%s&key=%s" % (url, secrets.bingKey)
 
response = requests.get(url, stream=True)
print(response)
dbutil.insert(dbPath, qk, response.content)
 
del response
# 強制睡一會,防止bing服務器限制
sleepTime = random() * 3
time.sleep(sleepTime)
 
# 左上爲原點
tilePixelMax = quadkey.TileSystem.geo_to_pixel((lat_max, lon_max), tileZoom)
tilePixelMin = quadkey.TileSystem.geo_to_pixel((lat_min, lon_min), tileZoom)
print(tilePixelMax)
print(tilePixelMin)
 
tile_pixel_list = []
 
for x in range(tilePixelMin[0], tilePixelMax[0], 256):
for y in range(tilePixelMax[1], tilePixelMin[1], 246):
tile_pixel_list.append((x, y))
 
# 取決與服務器的硬件性能
thread_pause = 30
for i in range(len(tile_pixel_list)):
print("處理"+str(i))
_thread.start_new_thread(get_tiles_by_pixel,(tile_pixel_list[i],) )
 
if(i % thread_pause == (thread_pause-1)):
print("讓正常運行的線程執行完,睡眠開始")
time.sleep(5)
print("睡眠結束")
 
# _thread.start_new_thread( get_tiles_by_pixel, ( ) )
 
 
print('下載完畢')

 

能夠優化的點不少多線程

  • 修改線程使用方式
  • 提升查詢影像是否存在的效率
  • 減小創建sqlite鏈接的次數

源碼

更多的詳情見小專欄文章GIS之家小專欄app

文章尾部提供源代碼下載,對本專欄感興趣的話,能夠關注一波dom

相關文章
相關標籤/搜索