Skip to content

Instantly share code, notes, and snippets.

@farhadinima75
Last active October 3, 2023 17:35
Show Gist options
  • Save farhadinima75/c3e73f8d7d9f8d2e452063e611fb9a8c to your computer and use it in GitHub Desktop.
Save farhadinima75/c3e73f8d7d9f8d2e452063e611fb9a8c to your computer and use it in GitHub Desktop.
A python script to download high resolution Google map images given a longitude, latitude and zoom level (Works with Python 3).
#!/usr/bin/python
# GoogleMapDownloader.py
# Created by Hayden Eskriett [http://eskriett.com]
# Edited by Nima Farhadi
#
# A script which when given a longitude, latitude and zoom level downloads a
# high resolution google map
# Find the associated blog post at: http://blog.eskriett.com/2013/07/19/downloading-google-maps/
from PIL import Image
import math, shutil, requests, os
class GoogleMapsLayers:
ROADMAP = "v"
TERRAIN = "p"
ALTERED_ROADMAP = "r"
SATELLITE = "s"
TERRAIN_ONLY = "t"
HYBRID = "y"
class GoogleMapDownloader:
"""
A class which generates high resolution google maps images given
a longitude, latitude and zoom level
"""
def __init__(self, lat, lng, zoom=12, layer=GoogleMapsLayers.ROADMAP):
"""
GoogleMapDownloader Constructor
Args:
lat: The latitude of the location required
lng: The longitude of the location required
zoom: The zoom level of the location required, ranges from 0 - 23
defaults to 12
"""
self._lat = lat
self._lng = lng
self._zoom = zoom
self._layer = layer
def getXY(self):
"""
Generates an X,Y tile coordinate based on the latitude, longitude
and zoom level
Returns: An X,Y tile coordinate
"""
tile_size = 256
# Use a left shift to get the power of 2
# i.e. a zoom level of 2 will have 2^2 = 4 tiles
numTiles = 1 << self._zoom
# Find the x_point given the longitude
point_x = (tile_size / 2 + self._lng * tile_size / 360.0) * numTiles // tile_size
# Convert the latitude to radians and take the sine
sin_y = math.sin(self._lat * (math.pi / 180.0))
# Calulate the y coorindate
point_y = ((tile_size / 2) + 0.5 * math.log((1 + sin_y) / (1 - sin_y)) * -(
tile_size / (2 * math.pi))) * numTiles // tile_size
return int(point_x), int(point_y)
def generateImage(self, **kwargs):
"""
Generates an image by stitching a number of google map tiles together.
Args:
start_x: The top-left x-tile coordinate
start_y: The top-left y-tile coordinate
tile_width: The number of tiles wide the image should be -
defaults to 5
tile_height: The number of tiles high the image should be -
defaults to 5
Returns:
A high-resolution Goole Map image.
"""
start_x = kwargs.get('start_x', None)
start_y = kwargs.get('start_y', None)
tile_width = kwargs.get('tile_width', 8)
tile_height = kwargs.get('tile_height', 8)
# Check that we have x and y tile coordinates
if start_x == None or start_y == None:
start_x, start_y = self.getXY()
# Determine the size of the image
width, height = 256 * tile_width, 256 * tile_height
# Create a new image of the size require
map_img = Image.new('RGB', (width, height))
for x in range(-tile_width//2, tile_width//2):
for y in range(-tile_height//2, tile_height//2):
url = f'https://mt0.google.com/vt?lyrs={self._layer}&x=' + str(start_x + x) + \
'&y=' + str(start_y + y) + '&z=' + str(self._zoom)
current_tile = str(x) + '-' + str(y)
response = requests.get(url, stream=True)
with open(current_tile, 'wb') as out_file: shutil.copyfileobj(response.raw, out_file)
im = Image.open(current_tile)
map_img.paste(im, ((x+tile_width//2) * 256, (y+tile_height//2) * 256))
os.remove(current_tile)
print('Image size (pix): ', map_img.size)
return map_img
def main():
# Create a new instance of GoogleMap Downloader
gmd = GoogleMapDownloader(51.5171, 0.1062, 13, GoogleMapsLayers.SATELLITE)
print("The tile coorindates are {}".format(gmd.getXY()))
try:
# Get the high resolution image
img = gmd.generateImage()
except IOError:
print("Could not generate the image - try adjusting the zoom level and checking your coordinates")
else:
# Save the image to disk
img.save("high_resolution_image.png")
print("The map has successfully been created")
if __name__ == '__main__': main()
@farhadinima75
Copy link
Author

hi @ws0352
If you changed lat/lon value, try zoom level between 16-19. Some places don't have high-level resolution images.

And if the problem still exists, delete try/except to see the exact error.

And also, feel free for asking further questions.

@zeeshan7929
Copy link

Error I GOT

b'<title>Sorry...</title><style> body { font-family: verdana, arial, sans-serif; background-color: #fff; color: #000; }</style>

Google
Sorry...

We're sorry...

... but your computer or network may be sending automated queries. To protect our users, we can't process your request right now.

See Google Help for more information.

'

@farhadinima75
Copy link
Author

Hi @zeeshan7929
Can you tell me what parameters did you use (I mean, zoom level, tile size,...)?

@Anjali1808
Copy link

How do we get to know the following information from the downloaded images :
1.) What is the spatail resolution of the image ?
2.) What date is the image captured ?
3.) Which satellite has been used to capture the image ?

Also, can we go back in time and download the images for the past years ?

@mwlock
Copy link

mwlock commented Jul 25, 2022

Is it possible to return lat/lon coordinates grid for the image? 😄

@GoroYeh56
Copy link

I got the error msg below:

zoom level: 13
Input filename: 0000000678
(lat, long): 49.018233566207, 8.4454809774168
The tile coorindates are (4288, 2812)
Traceback (most recent call last):
  File "/Users/goroyeh56/ROB590/getSatImg.py", line 204, in <module>
    if __name__ == '__main__':  main()
                                ^^^^^^
  File "/Users/goroyeh56/ROB590/getSatImg.py", line 197, in main
    img = gmd.generateImage()
          ^^^^^^^^^^^^^^^^^^^
  File "/Users/goroyeh56/ROB590/getSatImg.py", line 104, in generateImage
    urllib.request.urlretrieve(url, current_tile)
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/urllib/request.py", line 241, in urlretrieve
    with contextlib.closing(urlopen(url, data)) as fp:
                            ^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/urllib/request.py", line 216, in urlopen
    return opener.open(url, data, timeout)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/urllib/request.py", line 525, in open
    response = meth(req, response)
               ^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/urllib/request.py", line 634, in http_response
    response = self.parent.error(
               ^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/urllib/request.py", line 563, in error
    return self._call_chain(*args)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/urllib/request.py", line 496, in _call_chain
    result = func(*args)
             ^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/urllib/request.py", line 643, in http_error_default
    raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 403: Forbidde

Anyone knows what is happening?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment