Capture embedded google map image with Python without using a browser

前端 未结 8 1093
心在旅途
心在旅途 2020-12-04 18:57

I have noticed that, from Google Maps page, you can get an \"embed\" link to put inside an iframe and load the map in a browser. (no news here)

The image size can be

相关标签:
8条回答
  • 2020-12-04 19:10

    Rather than trying to use the embed link, you should go directly to the Google API to get images as static graphics. Here's the link to the Google Maps static image API - it looks like you can just pass in the long/lat parameters in the URL just as you do for the normal embeddable one. For example:

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

    gives you an 600x600 street-level overview centered on the co-ordinates you give above, which seems to be Porto Alegre in Brazil. Now you can use urlopen and PIL as Ned suggests:

    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)
    
    0 讨论(0)
  • 2020-12-04 19:15

    urllib.urlopen will open a URL, the result will have a .read() method you can use to get the image bytes. cStringIO has a file-like object based on a string in memory. PIL has an Image.open function that opens a file-like thing to give you an image object. Image objects can be asked about their pixel values.

    0 讨论(0)
  • 2020-12-04 19:16

    This is Daniel Roseman's answer for people that use python 3.x:

    • Assuming you got already Google Maps static image API

    Python 3.x code:

    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()
    
    0 讨论(0)
  • 2020-12-04 19:16

    @4Oh4's answer is right, but the maths are way more complicated than they need to be. Conversions between degrees and radians happen way more often than they need to. The Earth's radius is invoked for no reason at all—it cancels in all calculations. An offset is added to the pixel coordinates for no reason at all. The logo cutoff is way bigger than it needs to be. And a few other odds and ends, which have been written in the changes. Here's my version:

    #!/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()
    
    0 讨论(0)
  • 2020-12-04 19:17

    Edit: the code in this answer has been improved and simplified, here: https://stackoverflow.com/a/50536888/5859283


    Based on the excellent answer from heltonbiker with changes from BenElgar, below is some updated code for Python 3 and the addition of API key access, hope its useful for somebody:

    """
    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()
    
    0 讨论(0)
  • 2020-12-04 19:18

    The most simplest way to have the Google static map image captured/saved (as a 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()
    
    0 讨论(0)
提交回复
热议问题