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
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)
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.
This is Daniel Roseman's answer for people that use python 3.x:
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()
@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()
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()
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()