Capture embedded google map image with Python without using a browser

前端 未结 8 1098
心在旅途
心在旅途 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: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()
    

提交回复
热议问题