zoukankan      html  css  js  c++  java
  • Python获取 bing 地图发布自己的 TMS 服务(二)解决海量瓦片存取问题

    金字塔结构的瓦片数量有多大

    以目前互联网常用的WebMecator为例

    • 第一层:4幅256*256影像瓦片(JPG或PNG等)
    • 第二层:42
    • 第三层:43
    • 依次类推
      比如计算第1层至第18层的瓦片总数目(等比数列求和)91625968980个,大约916亿。存储空间估算在近百T。

    瓦片直接存储在文件系统中的缺点

    • 文件系统对文件数量、大小的限制
    • 不易迁移、备份
    • 等等

    解决方案

    这个问题本质上是对海量小数据的管理,很多互联网大厂都有比较成熟的方案,只需要根据具体情况进行选择调整即可。

    单机存储

    采用sqlite
    存储在多个sqlite中,sqlite文件名保证了唯一性,与(row,column, level)一一对应。

    • (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之家小专栏

    文章尾部提供源代码下载,对本专栏感兴趣的话,可以关注一波

  • 相关阅读:
    [Swift]LeetCode128. 最长连续序列 | Longest Consecutive Sequence
    [Swift]LeetCode126. 单词接龙 II | Word Ladder II
    [Swift]LeetCode127. 单词接龙 | Word Ladder
    [Xcode 实际操作]二、视图与手势-(6)给图像视图添加阴影效果
    [Swift]LeetCode124. 二叉树中的最大路径和 | Binary Tree Maximum Path Sum
    [Xcode 实际操作]二、视图与手势-(5)给图像视图添加圆角效果
    [Swift]LeetCode123. 买卖股票的最佳时机 III | Best Time to Buy and Sell Stock III
    [Swift]LeetCode120. 三角形最小路径和 | Triangle
    [Java]LeetCode117. 填充同一层的兄弟节点 II | Populating Next Right Pointers in Each Node II
    话说美国崛起
  • 原文地址:https://www.cnblogs.com/giserhome/p/11862886.html
Copyright © 2011-2022 走看看