下载特定区域内百度街景照片数据

本文介绍如何下载特定区域的百度街景照片,包括从OSM获取路网数据,采用ArcPy进行采点,WGS84到百度墨卡托坐标转换,获取全景图ID和元数据,最后下载全景图。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下载特定区域内百度街景照片数据

本文是在康博的博文的基础上再整理的。

01 下载路网数据

基本上都是使用 Open Street Map (OSM) 的路网数据。下载 OSM 数据的方法有很多,这里就不再赘述。

我个人是使用 OpenStreetMap Data Extracts 下载了整个中国的数据,然后用研究区的边界数据进行了裁剪。具体是使用了 ArcGIS 的 (ArcToolbox - Analysis Tools - Extract - Clip) 工具进行的裁剪。

02 对道路进行采点

使用 Ian Broad 开发的 Create Points on Polylines with ArcPy 工具箱进行采点。可以去工具箱的链接下载 tbx 文件,然后把文件复制到

C:\Users\Ivy\AppData\Roaming\ESRI\Desktop10.5\ArcToolbox\My Toolboxes

文件夹即可,在 Catalog 看到新下载的工具箱(上面的路径需要自己改一下,特别是用户名!)。

运行 CreatePointsLines 工具,设置参数,类型可以选“INTERVAL BY DISTANCE",然后 Distance 字段填 0.00001。这样大概就是每条路间隔 1 米取一个点。

然后在属性表里新建经纬度的字段

然后对字段进行【计算几何】的操作,把经纬度给算出来。

(康博的博文是把坐标系转换为了 wgs84 的 web 墨卡托坐标,但是我验证了一下,这个墨卡托坐标和百度使用的平面墨卡托坐标不是一致的,所以这里还是先输出 wgs84 的坐标,后面使用百度的 api 转换为 bd09mc。)

然后把属性表导出为 txt 文件。(不要导出为 dbf 文件,转换还挺麻烦的,用 python 的话需要额外的库,用 excel 的话数据量太大会打不开(excel 有行数限制))

接着把导出的点使用下面的代码存入数据库,我使用的是 mongodb。

import pandas as pd
import pymongo

df = pd.read_csv("../maps/points.txt")

new_df = df[["lon", "lat"]] # 只要坐标列
new_df = new_df[(new_df["lon"] != 0) & (new_df["lat"] != 0)] # 删除计算错误的点

new_df["wgs84"] = new_df["lon"].map(str)+","+new_df["lat"].map(str)
new_df["ok"] = 0
del new_df["lon"], new_df["lat"]
print(new_df.shape)
# 把读出来的 dataframe 转化为 dict,形式为[{"wgs84": "x,y",  "ok": 0}, ...]
data = new_df.to_dict(orient = 'records')

# 存入数据库
client = pymongo.MongoClient("mongodb://localhost:27017/")
db = client["area"]
col = db["rpoints_wgs84"]

col.insert_many(data)

数据库内的形式大概是如下所示。ok 字段是记录状态的。因为当点很多的时候,可能由于意外导致程序崩溃或者打断,而不知道程序的进度来重新启动。

03 WGS84 转换为百度墨卡托坐标

使用百度官方的 api 把 wgs84 坐标系转换为百度墨卡托坐标。

需要使用百度的开发者 ak,我是创建了一个形如下面这样的 json 文件来存储可用的 ak,方便程序调用。

{
    "ak": [
        "ak1",
        "ak2"
    ]
}

转换坐标的代码如下。因为 api 一次可以接收 100 个点,所以用到了均分列表的函数。

import requests, json
import pymongo
import sys, traceback
import random, time
from tqdm import tqdm

def convert_points(point):
    # 每次 100 个点
    coords = ";".join(point)
    url = "http://api.map.baidu.com/geoconv/v1/?coords={}&from=1&to=6&ak={}".format(coords, random.choice(aks))
    while True:
        try:
            res = requests.get(url)
            data = res.json()

            if data["status"] == 0:
                return data["result"]
            else:
                print(data)
        except (requests.exceptions.ConnectionError, json.decoder.JSONDecodeError) as e:
            print("\n Error: ", repr(e))
        except:
            print("\n ************************ Alert!! ********************************")
            traceback.print_exc()
            return False

def write_data(mc_point):
    client = pymongo.MongoClient("mongodb://localhost:27017/")
    db = client["area"]
    col = db["rpoints_mc"]

    data = []
    for point in mc_point:
        data.append({"bd09mc": "{},{}".format(point["x"], point["y"]), "ok": 0})

    now = time.time()
    print("Ready to insert data")
    while True:
        try:
            if len(data) <= 1000:
                col.insert_many(data)
            else:
                groups = split_list(data, n=1000)
                for group in groups:
                    col.insert_many(group)
            print("Inserting data used", time.time()-now,"s")
            break
        except:
            traceback.print_exc()
            sys.exit(1)

def split_list(l, n=100):
    # 均分列表
    new_l = []
    for i in range(0,len(l),n):
        new_l.append(l[i:i+n])
    return new_l

def get_points():
    client = pymongo.MongoClient("mongodb://localhost:27017/")
    db = client["area"]
    col = db["rpoints_wgs84"]

    docs = col.find({"ok": 0})
    points = [doc["wgs84"] for doc in docs]
    return points

if __name__=="__main__":
    ## 读取出点坐标
    wgs_points = get_points()
    points_groups = split_list(wgs_points)
    print("There're", len(points_groups), "groups")

    ## 读取出备用的 ak
    with open("ak.json", 'r', encoding='utf8') as f:
        j = json.load(f)
    aks = j["ak"]

    valid_points = []
    for wgs_point in tqdm(points_groups, ncols=80):
        while True:
            mc_point = convert_points(wgs_point)
            if mc_point:
                valid_points += mc_point
                break
        
    write_data(valid_points)

04 获取全景图 id

这时,使用的是一个非官方的链接来访问这个坐标点是否存在全景图(不同坐标点可能存在同一全景图)。

https://mapsv0.bdimg.com/?qt=qsdata&x={}&y={}

返回的是 json 数据,含有全景图的 id。

使用下面的代码多线程爬取全景图 id,并存入数据库,这里仍然使用的是 mongdb。

import pymongo
import threading
from queue import Queue
from threading import Thread
import requests, urllib3
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
import sys, traceback
from fake_useragent import UserAgent # 构造假的 Headers
from ippool.ippool_fast import * # 自己写的代理池的包
import time
import random

class Spider():
    def __init__(self):
        self.thread_num = 10
        self.start = time.time()
        self.all_panoids = self.get_panoids()
        
        # 各种队列
        self.point_q = self.get_points() # 待确认的点的队列
        self.all_lenth = self.point_q.qsize() # 队列总长度
        self.finish_q = Queue(100000) # 已经确认的点的队列
        self.panoid_q = Queue() # 全景图id队列
        self.ippool_q = Queue() # 代理池队列

    def get_points(self):
        client = pymongo.MongoClient("mongodb://localhost:27017/")
        db = client["area"]
        col = db["rpoints_mc"]

        docs = col.find({"ok": 0})

        point_q = Queue()
        for doc in docs:
            point_q.put(doc["bd09mc"])
        return point_q
    
    def get_panoids(self):
        client = pymongo.MongoClient("mongodb://localhost:27017/")
        db = client["area"]
        col = db["streetview"]

        docs = col.find()
        if docs:
            print(len(list(docs)))
            panoids = [doc["panoid"] for doc in docs]
            return panoids
        return []
    
    def writeData(self, panoids):
        client = pymongo.MongoClient("mongodb://localhost:27017/")
        db = client["area"]
        col = db["streetview"]

        docs = []
        for panoid in panoids:
            docs.append({"panoid": panoid,
                        "bd09mc": "",
                        "bd09ll": "",
                        "wgs84": "",
                        "date": "",
                        "ok": 0,
                        "info": {}})
        col.insert_many(docs)
    
    def req(self, url):
        headers = {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
            "Accept-Encoding": "gzip, deflate, br",
            "Accept-Language": "en-US,en;q=0.9",
            "DNT": "1",
            "Host": "mapsv0.bdimg.com",
            "
把网上电子地图的图片下载到本地. 因为网上电子图片是分缩放级别的,可以直接将所有级别或才定制级别的图片下载下来,会放到相应的文件夹里面.可供使用百度地图引擎的程序直接使用. 不好意思,定这么高的分,如果您分不够,可以给我发邮件,我把程序发给您. 双面GetBaiduMapPic\bin\Debug\GetBaiduMapPic.exe来运行程序, 第一步:填写好经度开始,经度结束。 第二步:填写好纬度开始,纬度结束。 第三步:选择缩放级别开始,也就是从第几级的缩放开始下载。 第四步:选择绽放级别结束,也就是到第几级的缩放结束下载百度的地图缩放是0-13个级别,我们可以从中选择。只要开始不要大于结束即可。 第五步:点击“开始生成URL”按钮。此时在下面左边的列表里面会显示出生成要下载的图片地址列表。 第六步:点击“选择存储位置”按钮,找到一个位置,用来存放配置文件及要下载的图片的位置。不要忘记文件名处要填写一个名字,自己随意起就好了。填好后点击保存。 第七步:点击“导出XML”按钮,这样便会将刚刚显示的要下载的文件列表保存到了XML文件中。 第八步:点击“开始获取”按钮,便会将百度的电子地图图片下载到本地了。就是您刚才放置XML文件位置。 如果有失败的地址,则点击“失败地址导出XML”,便可以将地址导出。以便重新进行读取下载。 在使用中如果遇到问题可以致 Email:lyx830621@163.com 声明:本程序只用作技术研究,软件使用自愿,若引起百度等网站纠纷与本人无关,请自行决定是否使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值