How to add a margin around markers in the Google Static Maps API using Python
This example shows how to use Python to generate a Google Static Map URL for a map that contains markers within some dimensions which are smaller than the map image dimensions. This effectively allows for setting minimum X and Y margins around the markers in a map. This is useful for a "fluid" web design where a maximum map size is requested from Google and is then cut off at the edges for small browser windows.
The bulk of this solution is based on the Javascript code here: http://stackoverflow.com/questions/6048975/google-maps-v3-how-to-calculate-the-zoom-level-for-a-given-bounds
import math
def generate_map_url(
min_map_width_px,
max_map_width_px,
min_map_height_px,
max_map_height_px,
marker_groups):
"""
Return a Google Static Map URL for a map that contains markers within
some dimensions which are smaller than the map image dimensions. This
effectively allows for setting minimum X and Y margins around the markers
in a map. This is useful for a "fluid" web design where a maximum map
size is requested from Google and is then cut off at the edges for
small browser windows.
"""
# Determine the maximum zoom to contain markers at the minimum map size
lat_list = [
lat for markers in marker_groups for lat, lng in markers['lat_lng']]
lng_list = [
lng for markers in marker_groups for lat, lng in markers['lat_lng']]
max_zoom = get_zoom_to_fit(
min(lat_list), max(lat_list), min(lng_list), max(lng_list),
min_map_width_px, min_map_height_px,
)
# Build the markers query string arguments
markers_args = ''
for markers in marker_groups:
lat_lng = '|'.join([
'{},{}'.format(lat, lng) for lat, lng in markers['lat_lng']])
markers_args += '&markers;=color:{}|{}'.format(markers['color'], lat_lng)
# Build and return the map URL
return ''.join([
'http://maps.googleapis.com/maps/api/staticmap',
'?sensor=false&v;=3&visual;_refresh=true',
'&size;={}x{}&zoom;={}'.format(
max_map_width_px, max_map_height_px, max_zoom),
markers_args,
])
def get_zoom_to_fit(min_lat, max_lat, min_lng, max_lng, width_px, height_px):
"""
Return the maximum zoom that will fit the given min/max lat/lng
coordinates in a map of the given dimensions. This is used to
override the zoom set by Google's implicit positioning.
Calculation translated from Javascript to Python from:
http://stackoverflow.com/questions/6048975/google-maps-v3-how-to-calculate-the-zoom-level-for-a-given-bounds
"""
GOOGLE_WORLD_WIDTH = 256
GOOGLE_WORLD_HEIGHT = 256
MAX_ZOOM = 17
def lat2rad(lat):
sinlat = math.sin(math.radians(lat))
radx2 = math.log((1 + sinlat) / (1 - sinlat)) / 2.0
return max(min(radx2, math.pi), -math.pi) / 2.0
def zoom(map_px, world_px, fraction):
# Use int() to round down to the nearest integer
return int(
math.log(float(map_px) / float(world_px) / fraction)
/ math.log(2.0)
)
# Determine the maximum zoom based on height and latitude
if min_lat == max_lat:
lat_zoom = MAX_ZOOM
else:
lat_fraction = (lat2rad(max_lat) - lat2rad(min_lat)) / math.pi
lat_zoom = zoom(height_px, GOOGLE_WORLD_HEIGHT, lat_fraction)
# Determine the maximum zoom based on width and longitude
if min_lng == max_lng:
lng_zoom = MAX_ZOOM
else:
lng_range = max_lng - min_lng
if lng_range < 0:
lng_range += 360.0
lng_fraction = lng_range / 360.0
lng_zoom = zoom(width_px, GOOGLE_WORLD_WIDTH, lng_fraction)
return min(lat_zoom, lng_zoom, MAX_ZOOM)
Here is an example:
map_url = generate_map_url(
min_map_width_px=240, max_map_width_px=380,
min_map_height_px=285, max_map_height_px=325,
marker_groups=[
{'color': 'blue',
'lat_lng': [(34.0993, -118.8394)]},
{'color': 'orange',
'lat_lng': [
(34.3997, -119.2002),
(34.5389, -118.4499),
(34.0983, -118.1285),
(33.5932, -117.9455),
(33.8322, -117.3958),
]}
]
)
print map_url
Here is a map without the margin: http://maps.googleapis.com/maps/api/staticmap?sensor=false&v;=3&visual;_refresh=true&size;=380x325&markers;=color:blue|34.0993,-118.8394&markers;=color:orange|34.3997,-119.2002|34.5389,-118.4499|34.0983,-118.1285|33.5932,-117.9455|33.8322,-117.3958
Here is the result with the margin: http://maps.googleapis.com/maps/api/staticmap?sensor=false&v;=3&visual;_refresh=true&size;=380x325&zoom;=7&markers;=color:blue|34.0993,-118.8394&markers;=color:orange|34.3997,-119.2002|34.5389,-118.4499|34.0983,-118.1285|33.5932,-117.9455|33.8322,-117.3958