16

我注意到,从谷歌地图页面,您可以获得一个“嵌入”链接以放入 iframe 并在浏览器中加载地图。(这里没有消息)

图像大小可以调整为非常大,因此我有兴趣将一些大图像作为单个 .PNG 获取。

更具体地说,我想从边界框(右上和左下坐标)定义一个矩形区域,并获得具有适当缩放因子的相应图像。

但我的问题是:如何使用 Python 来获取此地图的“像素内容”作为图像对象?

(我的理由是:如果浏览器可以获取和渲染这样的图像内容,那么 Python 也应该能够做到)。

编辑:这是显示我的示例地图的 HTML 文件的内容:

<iframe 
    width="2000"
    height="1500"
    frameborder="0"
    scrolling="yes"
    marginheight="0"
    marginwidth="0"
    src="http://maps.google.com.br/maps?hl=pt-BR&amp;ll=-30.027489,-51.229248&amp;spn=1.783415,2.745209&amp;z=10&amp;output=embed"/>

编辑:我按照 Ned Batchelder 的建议做了,并使用从上面的 iframe 中获取urllib.urlopen()的地址读取了呼叫的内容。src结果是大量的 javascript 代码,我认为这与 Google Maps JavaScript API 有关。所以,问题仍然存在:我怎样才能从 Python 中的所有这些东西中做一些有用的东西来获取地图图像?

编辑:这个链接似乎包含一些关于谷歌地图如何平铺地图的非常相关的信息:http: //www.codeproject.com/KB/scrapbook/googlemap.aspx

也: http ://econym.org.uk/gmap/howitworks.htm

4

8 回答 8

26

我感谢所有的答案。我最终以另一种方式解决了这个问题,使用 Google Maps Static API 和一些公式将坐标空间转换为像素空间,这样我就可以获得可以很好地“缝合”在一起的精确图像。

对于任何感兴趣的人,这里是代码。如果对某人有帮助,请发表评论!

==============================

import Image, urllib, StringIO
from math import log, exp, tan, atan, pi, ceil

EARTH_RADIUS = 6378137
EQUATOR_CIRCUMFERENCE = 2 * pi * EARTH_RADIUS
INITIAL_RESOLUTION = EQUATOR_CIRCUMFERENCE / 256.0
ORIGIN_SHIFT = EQUATOR_CIRCUMFERENCE / 2.0

def latlontopixels(lat, lon, zoom):
    mx = (lon * ORIGIN_SHIFT) / 180.0
    my = log(tan((90 + lat) * pi/360.0))/(pi/180.0)
    my = (my * ORIGIN_SHIFT) /180.0
    res = INITIAL_RESOLUTION / (2**zoom)
    px = (mx + ORIGIN_SHIFT) / res
    py = (my + ORIGIN_SHIFT) / res
    return px, py

def pixelstolatlon(px, py, zoom):
    res = INITIAL_RESOLUTION / (2**zoom)
    mx = px * res - ORIGIN_SHIFT
    my = py * res - ORIGIN_SHIFT
    lat = (my / ORIGIN_SHIFT) * 180.0
    lat = 180 / pi * (2*atan(exp(lat*pi/180.0)) - pi/2.0)
    lon = (mx / ORIGIN_SHIFT) * 180.0
    return lat, lon

############################################

# a neighbourhood in Lajeado, Brazil:

upperleft =  '-29.44,-52.0'  
lowerright = '-29.45,-51.98'

zoom = 18   # be careful not to get too many images!

############################################

ullat, ullon = map(float, upperleft.split(','))
lrlat, lrlon = map(float, lowerright.split(','))

# Set some important parameters
scale = 1
maxsize = 640

# convert all these coordinates to pixels
ulx, uly = latlontopixels(ullat, ullon, zoom)
lrx, lry = latlontopixels(lrlat, lrlon, zoom)

# calculate total pixel dimensions of final image
dx, dy = lrx - ulx, uly - lry

# calculate rows and columns
cols, rows = int(ceil(dx/maxsize)), int(ceil(dy/maxsize))

# calculate pixel dimensions of each small image
bottom = 120
largura = int(ceil(dx/cols))
altura = int(ceil(dy/rows))
alturaplus = altura + bottom


final = Image.new("RGB", (int(dx), int(dy)))
for x in range(cols):
    for y in range(rows):
        dxn = largura * (0.5 + x)
        dyn = altura * (0.5 + y)
        latn, lonn = pixelstolatlon(ulx + dxn, uly - dyn - bottom/2, zoom)
        position = ','.join((str(latn), str(lonn)))
        print x, y, position
        urlparams = urllib.urlencode({'center': position,
                                      'zoom': str(zoom),
                                      'size': '%dx%d' % (largura, alturaplus),
                                      'maptype': 'satellite',
                                      'sensor': 'false',
                                      'scale': scale})
        url = 'http://maps.google.com/maps/api/staticmap?' + urlparams
        f=urllib.urlopen(url)
        im=Image.open(StringIO.StringIO(f.read()))
        final.paste(im, (int(x*largura), int(y*altura)))
final.show()
于 2011-10-27T17:29:37.697 回答
16

与其尝试使用嵌入链接,不如直接使用 Google API 将图像作为静态图形获取。这是指向Google Maps 静态图像 API的链接- 看起来您只需在 URL 中传递 long/lat 参数,就像您对普通可嵌入的那样。例如:

http://maps.googleapis.com/maps/api/staticmap?center=-30.027489,-51.229248&size=600x600&zoom=14&sensor=false

以您在上面给出的坐标为中心,为您提供 600x600 的街道概览,这似乎是巴西的阿雷格里港。现在您可以按照 Ned 的建议使用urlopenand :PIL

from cStringIO import StringIO
import Image
import urllib

url = "http://maps.googleapis.com/maps/api/staticmap?center=-30.027489,-51.229248&size=800x800&zoom=14&sensor=false"
buffer = StringIO(urllib.urlopen(url).read())
image = Image.open(buffer)
于 2011-09-20T20:29:57.613 回答
8

编辑:此答案中的代码已得到改进和简化,此处:https ://stackoverflow.com/a/50536888/5859283


基于 heltonbiker 的出色回答以及 BenElgar 的更改,以下是 Python 3 的一些更新代码以及 API 密钥访问的添加,希望它对某人有用:

"""
Stitch together Google Maps images from lat, long coordinates
Based on work by heltonbiker and BenElgar
Changes: 
  * updated for Python 3
  * added Google Cloud Static Maps API key field (now required for access)
  * handle http request exceptions
"""

import requests
from io import BytesIO
from math import log, exp, tan, atan, pi, ceil
from PIL import Image
import sys

EARTH_RADIUS = 6378137
EQUATOR_CIRCUMFERENCE = 2 * pi * EARTH_RADIUS
INITIAL_RESOLUTION = EQUATOR_CIRCUMFERENCE / 256.0
ORIGIN_SHIFT = EQUATOR_CIRCUMFERENCE / 2.0
GOOGLE_MAPS_API_KEY = 'change this to your API key'

def latlontopixels(lat, lon, zoom):
    mx = (lon * ORIGIN_SHIFT) / 180.0
    my = log(tan((90 + lat) * pi/360.0))/(pi/180.0)
    my = (my * ORIGIN_SHIFT) /180.0
    res = INITIAL_RESOLUTION / (2**zoom)
    px = (mx + ORIGIN_SHIFT) / res
    py = (my + ORIGIN_SHIFT) / res
    return px, py

def pixelstolatlon(px, py, zoom):
    res = INITIAL_RESOLUTION / (2**zoom)
    mx = px * res - ORIGIN_SHIFT
    my = py * res - ORIGIN_SHIFT
    lat = (my / ORIGIN_SHIFT) * 180.0
    lat = 180 / pi * (2*atan(exp(lat*pi/180.0)) - pi/2.0)
    lon = (mx / ORIGIN_SHIFT) * 180.0
    return lat, lon


def get_maps_image(NW_lat_long, SE_lat_long, zoom=18):
  
  ullat, ullon = NW_lat_long
  lrlat, lrlon = SE_lat_long
  
  # Set some important parameters
  scale = 1
  maxsize = 640
  
  # convert all these coordinates to pixels
  ulx, uly = latlontopixels(ullat, ullon, zoom)
  lrx, lry = latlontopixels(lrlat, lrlon, zoom)
  
  # calculate total pixel dimensions of final image
  dx, dy = lrx - ulx, uly - lry
  
  # calculate rows and columns
  cols, rows = int(ceil(dx/maxsize)), int(ceil(dy/maxsize))
  
  # calculate pixel dimensions of each small image
  bottom = 120
  largura = int(ceil(dx/cols))
  altura = int(ceil(dy/rows))
  alturaplus = altura + bottom
  
  # assemble the image from stitched
  final = Image.new("RGB", (int(dx), int(dy)))
  for x in range(cols):
      for y in range(rows):
          dxn = largura * (0.5 + x)
          dyn = altura * (0.5 + y)
          latn, lonn = pixelstolatlon(ulx + dxn, uly - dyn - bottom/2, zoom)
          position = ','.join((str(latn), str(lonn)))
          print(x, y, position)
          urlparams = {'center': position,
                        'zoom': str(zoom),
                        'size': '%dx%d' % (largura, alturaplus),
                        'maptype': 'satellite',
                        'sensor': 'false',
                        'scale': scale}
          if GOOGLE_MAPS_API_KEY is not None:
            urlparams['key'] = GOOGLE_MAPS_API_KEY
            
          url = 'http://maps.google.com/maps/api/staticmap'
          try:                  
            response = requests.get(url, params=urlparams)
            response.raise_for_status()
          except requests.exceptions.RequestException as e:
            print(e)
            sys.exit(1)
            
          im = Image.open(BytesIO(response.content))                  
          final.paste(im, (int(x*largura), int(y*altura)))
          
  return final

############################################

if __name__ == '__main__':
  
  # a neighbourhood in Lajeado, Brazil:
  NW_lat_long =  (-29.44,-52.0)
  SE_lat_long = (-29.45,-51.98)
  
  zoom = 18   # be careful not to get too many images!
  
  result = get_maps_image(NW_lat_long, SE_lat_long, zoom=18)
  result.show()
于 2017-12-12T15:46:41.163 回答
7

@4Oh4 的答案是正确的,但数学比他们需要的要复杂得多。度数和弧度之间的转换比他们需要的更频繁。地球的半径被无缘无故地调用——它在所有计算中都被取消了。无缘无故地向像素坐标添加了偏移量。徽标的截断比它需要的要大得多。以及其他一些零碎的东西,已经写在了改动中。这是我的版本:

#!/usr/bin/env python
"""
Stitch together Google Maps images from lat, long coordinates
Based on work by heltonbiker and BenElgar
Changes: 
* updated for Python 3
* added Google Maps API key (compliance with T&C, although can set to None)
* handle http request exceptions

With contributions from Eric Toombs.
Changes:
* Dramatically simplified the maths.
* Set a more reasonable default logo cutoff.
* Added global constants for logo cutoff and max image size.
* Translated a couple presumably Portuguese variable names to English.
"""

import requests
from io import BytesIO
from math import log, exp, tan, atan, ceil
from PIL import Image
import sys

# circumference/radius
tau = 6.283185307179586
# One degree in radians, i.e. in the units the machine uses to store angle,
# which is always radians. For converting to and from degrees. See code for
# usage demonstration.
DEGREE = tau/360

ZOOM_OFFSET = 8
GOOGLE_MAPS_API_KEY = None  # set to 'your_API_key'

# Max width or height of a single image grabbed from Google.
MAXSIZE = 640
# For cutting off the logos at the bottom of each of the grabbed images.  The
# logo height in pixels is assumed to be less than this amount.
LOGO_CUTOFF = 32


def latlon2pixels(lat, lon, zoom):
    mx = lon
    my = log(tan((lat + tau/4)/2))
    res = 2**(zoom + ZOOM_OFFSET) / tau
    px = mx*res
    py = my*res
    return px, py

def pixels2latlon(px, py, zoom):
    res = 2**(zoom + ZOOM_OFFSET) / tau
    mx = px/res
    my = py/res
    lon = mx
    lat = 2*atan(exp(my)) - tau/4
    return lat, lon


def get_maps_image(NW_lat_long, SE_lat_long, zoom=18):

    ullat, ullon = NW_lat_long
    lrlat, lrlon = SE_lat_long

    # convert all these coordinates to pixels
    ulx, uly = latlon2pixels(ullat, ullon, zoom)
    lrx, lry = latlon2pixels(lrlat, lrlon, zoom)

    # calculate total pixel dimensions of final image
    dx, dy = lrx - ulx, uly - lry

    # calculate rows and columns
    cols, rows = ceil(dx/MAXSIZE), ceil(dy/MAXSIZE)

    # calculate pixel dimensions of each small image
    width = ceil(dx/cols)
    height = ceil(dy/rows)
    heightplus = height + LOGO_CUTOFF

    # assemble the image from stitched
    final = Image.new('RGB', (int(dx), int(dy)))
    for x in range(cols):
        for y in range(rows):
            dxn = width * (0.5 + x)
            dyn = height * (0.5 + y)
            latn, lonn = pixels2latlon(
                    ulx + dxn, uly - dyn - LOGO_CUTOFF/2, zoom)
            position = ','.join((str(latn/DEGREE), str(lonn/DEGREE)))
            print(x, y, position)
            urlparams = {
                    'center': position,
                    'zoom': str(zoom),
                    'size': '%dx%d' % (width, heightplus),
                    'maptype': 'satellite',
                    'sensor': 'false',
                    'scale': 1
                }
            if GOOGLE_MAPS_API_KEY is not None:
                urlparams['key'] = GOOGLE_MAPS_API_KEY

            url = 'http://maps.google.com/maps/api/staticmap'
            try:                  
                response = requests.get(url, params=urlparams)
                response.raise_for_status()
            except requests.exceptions.RequestException as e:
                print(e)
                sys.exit(1)

            im = Image.open(BytesIO(response.content))                  
            final.paste(im, (int(x*width), int(y*height)))

    return final

############################################

if __name__ == '__main__':
    # a neighbourhood in Lajeado, Brazil:
    NW_lat_long =  (-29.44*DEGREE, -52.0*DEGREE)
    SE_lat_long = (-29.45*DEGREE, -51.98*DEGREE)

    zoom = 18   # be careful not to get too many images!

    result = get_maps_image(NW_lat_long, SE_lat_long, zoom=18)
    result.show()
于 2018-05-25T21:11:33.133 回答
6

这是Daniel Roseman对使用 python 3.x 的人的回答:

Python 3.x 代码:

from io import BytesIO
from PIL import Image
from urllib import request
import matplotlib.pyplot as plt # this is if you want to plot the map using pyplot

url = "http://maps.googleapis.com/maps/api/staticmap?center=-30.027489,-51.229248&size=800x800&zoom=14&sensor=false"

buffer = BytesIO(request.urlopen(url).read())
image = Image.open(buffer)

# Show Using PIL
image.show()

# Or using pyplot
plt.imshow(image)
plt.show()
于 2016-09-19T13:30:18.823 回答
3

更简洁的 Python 2.x 兼容方法是

from io import BytesIO
import Image
import urllib

url = "http://maps.googleapis.com/maps/api/staticmap?center=52.50058,13.31316&size=800x800&zoom=14"
buffer = BytesIO(urllib.urlopen(url).read())
image = Image.open(buffer)
image.save("map.png")
于 2016-11-10T13:20:15.343 回答
1

捕获/保存 Google 静态地图图像(作为 png)的最简单方法:

import requests

img = open('tmp.png','wb')
img.write(requests.get('https://maps.googleapis.com/maps/api/staticmap?center=33.0456,131.3009&zoom=12&size=320x385&key=YOUR_API_KEY').content)
img.close()
于 2017-09-27T20:29:28.307 回答
0

urllib.urlopen将打开一个 URL,结果将有一个.read()可用于获取图像字节的方法。 cStringIO有一个基于内存中字符串的类文件对象。PIL 有一个Image.open功能,可以打开一个类似文件的东西给你一个图像对象。可以询问图像对象的像素值。

于 2011-09-20T19:17:38.793 回答