简单的OpenStreetMap磁贴显示为Python

Ber*_*Git 7 python openstreetmap

我想在我的python代码中包含开放街道地图(OSM).

我已经阅读了很多关于OSM的网页.但不幸的是,我有点迷失,关于我最好用的包.

我正在寻找一种在我的应用程序中获取OSM图像的简单方法.作为我的起点,我想的是:

import matplotlib.pyplot as plt

# Pseudo - Code for required function 'GetOSMImage'
Map = GetOSMImage(lat,long,delta_lat,delta_long)

imgplot = plt.imshow(Map)
Run Code Online (Sandbox Code Playgroud)

后来我想在这个plt中添加我的附加数据.(我知道我需要处理预测等)

我不需要/想要的东西:

  • 在我自己的网站上显示
  • 将我的数据上传到某个Internet服务器
  • 缩放,滚动等交互式功能(首先)
  • 手动处理并从OSM呈现.xml数据
  • 首先,我不想定义渲染样式的每个细节.我希望/期望存在一些默认样式.

你有一个很好的起点吗?或者我低估了这个主题的复杂性?

Ber*_*Git 11

根据您的输入,我能够实现我的目标.这是我的其他代码,它们正在寻找OSM的起点.(科西嘉还有很大的改进空间).

import matplotlib.pyplot as plt
import numpy as np

import math
import urllib2
import StringIO
from PIL import Image



def deg2num(lat_deg, lon_deg, zoom):
  lat_rad = math.radians(lat_deg)
  n = 2.0 ** zoom
  xtile = int((lon_deg + 180.0) / 360.0 * n)
  ytile = int((1.0 - math.log(math.tan(lat_rad) + (1 / math.cos(lat_rad))) / math.pi) / 2.0 * n)
  return (xtile, ytile)

def num2deg(xtile, ytile, zoom):
  n = 2.0 ** zoom
  lon_deg = xtile / n * 360.0 - 180.0
  lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
  lat_deg = math.degrees(lat_rad)
  return (lat_deg, lon_deg)



def getImageCluster(lat_deg, lon_deg, delta_lat,  delta_long, zoom):
    smurl = r"http://a.tile.openstreetmap.org/{0}/{1}/{2}.png"
    xmin, ymax =deg2num(lat_deg, lon_deg, zoom)
    xmax, ymin =deg2num(lat_deg + delta_lat, lon_deg + delta_long, zoom)

    Cluster = Image.new('RGB',((xmax-xmin+1)*256-1,(ymax-ymin+1)*256-1) ) 
    for xtile in range(xmin, xmax+1):
        for ytile in range(ymin,  ymax+1):
            try:
                imgurl=smurl.format(zoom, xtile, ytile)
                print("Opening: " + imgurl)
                imgstr = urllib2.urlopen(imgurl).read()
                tile = Image.open(StringIO.StringIO(imgstr))
                Cluster.paste(tile, box=((xtile-xmin)*256 ,  (ytile-ymin)*255))
            except: 
                print("Couldn't download image")
                tile = None

    return Cluster



if __name__ == '__main__':

    a = getImageCluster(38.5, -77.04, 0.02,  0.05, 13)
    fig = plt.figure()
    fig.patch.set_facecolor('white')
    plt.imshow(np.asarray(a))
    plt.show()
Run Code Online (Sandbox Code Playgroud)

  • 请不要再使用 urllib2 请参阅 /sf/answers/2413255701/ (2认同)

etn*_*tna 7

建立在BerndGit的不错答案上,我添加了一个稍微修改过的版本,允许显示其他内容和图块(使用Basemap).顺便说一下,我遇到了一个专用的库,geotiler(http://wrobell.it-zone.org/geotiler/intro.html),但它需要Python 3.

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np

import math
import urllib2
import StringIO
from PIL import Image

def deg2num(lat_deg, lon_deg, zoom):
  lat_rad = math.radians(lat_deg)
  n = 2.0 ** zoom
  xtile = int((lon_deg + 180.0) / 360.0 * n)
  ytile = int((1.0 - math.log(math.tan(lat_rad) + (1 / math.cos(lat_rad))) / math.pi) / 2.0 * n)
  return (xtile, ytile)

def num2deg(xtile, ytile, zoom):
  """
  http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
  This returns the NW-corner of the square. 
  Use the function with xtile+1 and/or ytile+1 to get the other corners. 
  With xtile+0.5 & ytile+0.5 it will return the center of the tile.
  """
  n = 2.0 ** zoom
  lon_deg = xtile / n * 360.0 - 180.0
  lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
  lat_deg = math.degrees(lat_rad)
  return (lat_deg, lon_deg)

def getImageCluster(lat_deg, lon_deg, delta_lat,  delta_long, zoom):
    smurl = r"http://a.tile.openstreetmap.org/{0}/{1}/{2}.png"
    xmin, ymax = deg2num(lat_deg, lon_deg, zoom)
    xmax, ymin = deg2num(lat_deg + delta_lat, lon_deg + delta_long, zoom)

    bbox_ul = num2deg(xmin, ymin, zoom)
    bbox_ll = num2deg(xmin, ymax + 1, zoom)
    #print bbox_ul, bbox_ll

    bbox_ur = num2deg(xmax + 1, ymin, zoom)
    bbox_lr = num2deg(xmax + 1, ymax +1, zoom)
    #print bbox_ur, bbox_lr

    Cluster = Image.new('RGB',((xmax-xmin+1)*256-1,(ymax-ymin+1)*256-1) )
    for xtile in range(xmin, xmax+1):
        for ytile in range(ymin,  ymax+1):
            try:
                imgurl=smurl.format(zoom, xtile, ytile)
                print("Opening: " + imgurl)
                imgstr = urllib2.urlopen(imgurl).read()
                tile = Image.open(StringIO.StringIO(imgstr))
                Cluster.paste(tile, box=((xtile-xmin)*255 ,  (ytile-ymin)*255))
            except: 
                print("Couldn't download image")
                tile = None

    return Cluster, [bbox_ll[1], bbox_ll[0], bbox_ur[1], bbox_ur[0]]

if __name__ == '__main__':
    lat_deg, lon_deg, delta_lat,  delta_long, zoom = 45.720-0.04/2, 4.210-0.08/2, 0.04,  0.08, 14
    a, bbox = getImageCluster(lat_deg, lon_deg, delta_lat,  delta_long, zoom)

    fig = plt.figure(figsize=(10, 10))
    ax = plt.subplot(111)
    m = Basemap(
        llcrnrlon=bbox[0], llcrnrlat=bbox[1],
        urcrnrlon=bbox[2], urcrnrlat=bbox[3],
        projection='merc', ax=ax
    )
    # list of points to display (long, lat)
    ls_points = [m(x,y) for x,y in [(4.228, 45.722), (4.219, 45.742), (4.221, 45.737)]]
    m.imshow(a, interpolation='lanczos', origin='upper')
    ax.scatter([point[0] for point in ls_points],
               [point[1] for point in ls_points],
               alpha = 0.9)
    plt.show()
Run Code Online (Sandbox Code Playgroud)


glg*_*lgl 5

它不是那么复杂。从链接可以获得一些指导,其中详细介绍了图块的复杂性。

在这里几乎无法复制它,但是通常您必须

  • 通过公式确定所需的瓷砖
  • 从服务器上加载它们(可以选择某些地图样式)
  • 可能将它们双向连接
  • 然后显示它们。

请注意,您可能还必须解决长宽比问题。


ph1*_*1g0 5

编辑:OpenStreetMap 声明他们的 tile 服务器不能免费使用并且受使用政策的约束:
https : //operations.osmfoundation.org/policies/tiles/
请在使用示例之前阅读此内容。

由于我在 Python 3.8 中实现代码时遇到问题,我将一些答案合并在一起并修改了代码。现在它对我有用,我没有收到任何错误。
当我尝试在 Python 3 中运行来自 BerndGit 的原始代码时,我必须进行与他的回答中描述的 Joining Dots 相同的更改。我换了

 import urllib2
 import StringIO
Run Code Online (Sandbox Code Playgroud)

import requests
from io import BytesIO
Run Code Online (Sandbox Code Playgroud)

因为 urllib2 库不再适用于 Python 3。您必须使用 urllib.request 或 requests。
然后我不得不从 getImageCluster 函数中更改这两行

imgstr = urllib2.urlopen(imgurl).read()
tile = Image.open(StringIO.StringIO(imgstr))
Run Code Online (Sandbox Code Playgroud)

imgstr = requests.get(imgurl)
tile = Image.open(BytesIO(imgstr.content))
Run Code Online (Sandbox Code Playgroud)

之后,我可以无错误地运行代码,但仍然无法下载图像。结果我总是得到一块黑色的瓷砖。通过一些研究,我了解到在使用请求时伪造用户代理很重要,因为网站可以判断请求来自 Python 并且可能会阻止它。以下网站对此 进行了描述:
https : //www.scrapehero.com/how-to-fake-and-rotate-user-agents-using-python-3/
所以我按照网站上的建议添加了这一行就在 getImageCluster 函数的开头:

headers = {"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36"}
Run Code Online (Sandbox Code Playgroud)

现在我们需要将这些标头包含在请求调用中:

imgstr = requests.get(imgurl, headers=headers)
Run Code Online (Sandbox Code Playgroud)

整个代码现在看起来像这样:

import matplotlib.pyplot as plt
import numpy as np
import math
import requests
from io import BytesIO
from PIL import Image
   
    
    
def deg2num(lat_deg, lon_deg, zoom):
  lat_rad = math.radians(lat_deg)
  n = 2.0 ** zoom
  xtile = int((lon_deg + 180.0) / 360.0 * n)
  ytile = int((1.0 - math.log(math.tan(lat_rad) + (1 / math.cos(lat_rad))) / math.pi) / 2.0 * n)
  return (xtile, ytile)
    
def num2deg(xtile, ytile, zoom):
  n = 2.0 ** zoom
  lon_deg = xtile / n * 360.0 - 180.0
  lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
  lat_deg = math.degrees(lat_rad)
  return (lat_deg, lon_deg)
   
def getImageCluster(lat_deg, lon_deg, delta_lat,  delta_long, zoom):
    headers = {"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36"}
    smurl = r"http://a.tile.openstreetmap.org/{0}/{1}/{2}.png"
    xmin, ymax =deg2num(lat_deg, lon_deg, zoom)
    xmax, ymin =deg2num(lat_deg + delta_lat, lon_deg + delta_long, zoom)
   
    Cluster = Image.new('RGB',((xmax-xmin+1)*256-1,(ymax-ymin+1)*256-1) ) 
    for xtile in range(xmin, xmax+1):
        for ytile in range(ymin,  ymax+1):
            try:
                imgurl = smurl.format(zoom, xtile, ytile)
                print("Opening: " + imgurl)
                imgstr = requests.get(imgurl, headers=headers)
                tile = Image.open(BytesIO(imgstr.content))
                Cluster.paste(tile, box = ((xtile-xmin)*256 ,  (ytile-ymin)*255))
            except: 
                print("Couldn't download image")
                tile = None
   
    return Cluster
    
    
    
if __name__ == '__main__':
    
    a = getImageCluster(38.5, -77.04, 0.02,  0.05, 13)
    fig = plt.figure()
    fig.patch.set_facecolor('white')
    plt.imshow(np.asarray(a))
    plt.show()
Run Code Online (Sandbox Code Playgroud)

结果如下: 输出