diff options
Diffstat (limited to 'lib/python2.7/site-packages/django/contrib/gis/maps/google')
4 files changed, 767 insertions, 0 deletions
diff --git a/lib/python2.7/site-packages/django/contrib/gis/maps/google/__init__.py b/lib/python2.7/site-packages/django/contrib/gis/maps/google/__init__.py new file mode 100644 index 0000000..9be689c --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/maps/google/__init__.py @@ -0,0 +1,61 @@ +""" + This module houses the GoogleMap object, used for generating + the needed javascript to embed Google Maps in a Web page. + + Google(R) is a registered trademark of Google, Inc. of Mountain View, California. + + Example: + + * In the view: + return render_to_response('template.html', {'google' : GoogleMap(key="abcdefg")}) + + * In the template: + + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + {{ google.xhtml }} + <head> + <title>Google Maps via GeoDjango</title> + {{ google.style }} + {{ google.scripts }} + </head> + {{ google.body }} + <div id="{{ google.dom_id }}" style="width:600px;height:400px;"></div> + </body> + </html> + + Note: If you want to be more explicit in your templates, the following are + equivalent: + {{ google.body }} => "<body {{ google.onload }} {{ google.onunload }}>" + {{ google.xhtml }} => "<html xmlns="http://www.w3.org/1999/xhtml" {{ google.xmlns }}>" + {{ google.style }} => "<style>{{ google.vml_css }}</style>" + + Explanation: + - The `xhtml` property provides the correct XML namespace needed for + Google Maps to operate in IE using XHTML. Google Maps on IE uses + VML to draw polylines. Returns, by default: + <html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"> + + - The `style` property provides the correct style tag for the CSS + properties required by Google Maps on IE: + <style type="text/css">v\:* {behavior:url(#default#VML);}</style> + + - The `scripts` property provides the necessary <script> tags for + including the Google Maps javascript, as well as including the + generated javascript. + + - The `body` property provides the correct attributes for the + body tag to load the generated javascript. By default, returns: + <body onload="gmap_load()" onunload="GUnload()"> + + - The `dom_id` property returns the DOM id for the map. Defaults to "map". + + The following attributes may be set or customized in your local settings: + * GOOGLE_MAPS_API_KEY: String of your Google Maps API key. These are tied to + to a domain. May be obtained from http://www.google.com/apis/maps/ + * GOOGLE_MAPS_API_VERSION (optional): Defaults to using "2.x" + * GOOGLE_MAPS_URL (optional): Must have a substitution ('%s') for the API + version. +""" +from django.contrib.gis.maps.google.gmap import GoogleMap, GoogleMapSet +from django.contrib.gis.maps.google.overlays import GEvent, GIcon, GMarker, GPolygon, GPolyline +from django.contrib.gis.maps.google.zoom import GoogleZoom diff --git a/lib/python2.7/site-packages/django/contrib/gis/maps/google/gmap.py b/lib/python2.7/site-packages/django/contrib/gis/maps/google/gmap.py new file mode 100644 index 0000000..ff65626 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/maps/google/gmap.py @@ -0,0 +1,233 @@ +from django.conf import settings +from django.template.loader import render_to_string +from django.utils.html import format_html +from django.utils.safestring import mark_safe +from django.utils.six.moves import xrange + +from django.contrib.gis.maps.google.overlays import GPolygon, GPolyline, GMarker + +class GoogleMapException(Exception): + pass + + +# The default Google Maps URL (for the API javascript) +# TODO: Internationalize for Japan, UK, etc. +GOOGLE_MAPS_URL='http://maps.google.com/maps?file=api&v=%s&key=' + + +class GoogleMap(object): + "A class for generating Google Maps JavaScript." + + # String constants + onunload = mark_safe('onunload="GUnload()"') # Cleans up after Google Maps + vml_css = mark_safe('v\:* {behavior:url(#default#VML);}') # CSS for IE VML + xmlns = mark_safe('xmlns:v="urn:schemas-microsoft-com:vml"') # XML Namespace (for IE VML). + + def __init__(self, key=None, api_url=None, version=None, + center=None, zoom=None, dom_id='map', + kml_urls=[], polylines=None, polygons=None, markers=None, + template='gis/google/google-map.js', + js_module='geodjango', + extra_context={}): + + # The Google Maps API Key defined in the settings will be used + # if not passed in as a parameter. The use of an API key is + # _required_. + if not key: + try: + self.key = settings.GOOGLE_MAPS_API_KEY + except AttributeError: + raise GoogleMapException('Google Maps API Key not found (try adding GOOGLE_MAPS_API_KEY to your settings).') + else: + self.key = key + + # Getting the Google Maps API version, defaults to using the latest ("2.x"), + # this is not necessarily the most stable. + if not version: + self.version = getattr(settings, 'GOOGLE_MAPS_API_VERSION', '2.x') + else: + self.version = version + + # Can specify the API URL in the `api_url` keyword. + if not api_url: + self.api_url = getattr(settings, 'GOOGLE_MAPS_URL', GOOGLE_MAPS_URL) % self.version + else: + self.api_url = api_url + + # Setting the DOM id of the map, the load function, the JavaScript + # template, and the KML URLs array. + self.dom_id = dom_id + self.extra_context = extra_context + self.js_module = js_module + self.template = template + self.kml_urls = kml_urls + + # Does the user want any GMarker, GPolygon, and/or GPolyline overlays? + overlay_info = [[GMarker, markers, 'markers'], + [GPolygon, polygons, 'polygons'], + [GPolyline, polylines, 'polylines']] + + for overlay_class, overlay_list, varname in overlay_info: + setattr(self, varname, []) + if overlay_list: + for overlay in overlay_list: + if isinstance(overlay, overlay_class): + getattr(self, varname).append(overlay) + else: + getattr(self, varname).append(overlay_class(overlay)) + + # If GMarker, GPolygons, and/or GPolylines are used the zoom will be + # automatically calculated via the Google Maps API. If both a zoom + # level and a center coordinate are provided with polygons/polylines, + # no automatic determination will occur. + self.calc_zoom = False + if self.polygons or self.polylines or self.markers: + if center is None or zoom is None: + self.calc_zoom = True + + # Defaults for the zoom level and center coordinates if the zoom + # is not automatically calculated. + if zoom is None: zoom = 4 + self.zoom = zoom + if center is None: center = (0, 0) + self.center = center + + def render(self): + """ + Generates the JavaScript necessary for displaying this Google Map. + """ + params = {'calc_zoom' : self.calc_zoom, + 'center' : self.center, + 'dom_id' : self.dom_id, + 'js_module' : self.js_module, + 'kml_urls' : self.kml_urls, + 'zoom' : self.zoom, + 'polygons' : self.polygons, + 'polylines' : self.polylines, + 'icons': self.icons, + 'markers' : self.markers, + } + params.update(self.extra_context) + return render_to_string(self.template, params) + + @property + def body(self): + "Returns HTML body tag for loading and unloading Google Maps javascript." + return format_html('<body {0} {1}>', self.onload, self.onunload) + + @property + def onload(self): + "Returns the `onload` HTML <body> attribute." + return format_html('onload="{0}.{1}_load()"', self.js_module, self.dom_id) + + @property + def api_script(self): + "Returns the <script> tag for the Google Maps API javascript." + return format_html('<script src="{0}{1}" type="text/javascript"></script>', + self.api_url, self.key) + + @property + def js(self): + "Returns only the generated Google Maps JavaScript (no <script> tags)." + return self.render() + + @property + def scripts(self): + "Returns all <script></script> tags required with Google Maps JavaScript." + return format_html('{0}\n <script type="text/javascript">\n//<![CDATA[\n{1}//]]>\n </script>', + self.api_script, mark_safe(self.js)) + + @property + def style(self): + "Returns additional CSS styling needed for Google Maps on IE." + return format_html('<style type="text/css">{0}</style>', self.vml_css) + + @property + def xhtml(self): + "Returns XHTML information needed for IE VML overlays." + return format_html('<html xmlns="http://www.w3.org/1999/xhtml" {0}>', self.xmlns) + + @property + def icons(self): + "Returns a sequence of GIcon objects in this map." + return set([marker.icon for marker in self.markers if marker.icon]) + +class GoogleMapSet(GoogleMap): + + def __init__(self, *args, **kwargs): + """ + A class for generating sets of Google Maps that will be shown on the + same page together. + + Example: + gmapset = GoogleMapSet( GoogleMap( ... ), GoogleMap( ... ) ) + gmapset = GoogleMapSet( [ gmap1, gmap2] ) + """ + # The `google-multi.js` template is used instead of `google-single.js` + # by default. + template = kwargs.pop('template', 'gis/google/google-multi.js') + + # This is the template used to generate the GMap load JavaScript for + # each map in the set. + self.map_template = kwargs.pop('map_template', 'gis/google/google-single.js') + + # Running GoogleMap.__init__(), and resetting the template + # value with default obtained above. + super(GoogleMapSet, self).__init__(**kwargs) + self.template = template + + # If a tuple/list passed in as first element of args, then assume + if isinstance(args[0], (tuple, list)): + self.maps = args[0] + else: + self.maps = args + + # Generating DOM ids for each of the maps in the set. + self.dom_ids = ['map%d' % i for i in xrange(len(self.maps))] + + def load_map_js(self): + """ + Returns JavaScript containing all of the loading routines for each + map in this set. + """ + result = [] + for dom_id, gmap in zip(self.dom_ids, self.maps): + # Backup copies the GoogleMap DOM id and template attributes. + # They are overridden on each GoogleMap instance in the set so + # that only the loading JavaScript (and not the header variables) + # is used with the generated DOM ids. + tmp = (gmap.template, gmap.dom_id) + gmap.template = self.map_template + gmap.dom_id = dom_id + result.append(gmap.js) + # Restoring the backup values. + gmap.template, gmap.dom_id = tmp + return mark_safe(''.join(result)) + + def render(self): + """ + Generates the JavaScript for the collection of Google Maps in + this set. + """ + params = {'js_module' : self.js_module, + 'dom_ids' : self.dom_ids, + 'load_map_js' : self.load_map_js(), + 'icons' : self.icons, + } + params.update(self.extra_context) + return render_to_string(self.template, params) + + @property + def onload(self): + "Returns the `onload` HTML <body> attribute." + # Overloaded to use the `load` function defined in the + # `google-multi.js`, which calls the load routines for + # each one of the individual maps in the set. + return mark_safe('onload="%s.load()"' % self.js_module) + + @property + def icons(self): + "Returns a sequence of all icons in each map of the set." + icons = set() + for map in self.maps: icons |= map.icons + return icons diff --git a/lib/python2.7/site-packages/django/contrib/gis/maps/google/overlays.py b/lib/python2.7/site-packages/django/contrib/gis/maps/google/overlays.py new file mode 100644 index 0000000..b82d967 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/maps/google/overlays.py @@ -0,0 +1,311 @@ +from django.contrib.gis.geos import fromstr, Point, LineString, LinearRing, Polygon +from django.utils.functional import total_ordering +from django.utils.safestring import mark_safe +from django.utils import six +from django.utils.encoding import python_2_unicode_compatible + + +@python_2_unicode_compatible +class GEvent(object): + """ + A Python wrapper for the Google GEvent object. + + Events can be attached to any object derived from GOverlayBase with the + add_event() call. + + For more information please see the Google Maps API Reference: + http://code.google.com/apis/maps/documentation/reference.html#GEvent + + Example: + + from django.shortcuts import render_to_response + from django.contrib.gis.maps.google import GoogleMap, GEvent, GPolyline + + def sample_request(request): + polyline = GPolyline('LINESTRING(101 26, 112 26, 102 31)') + event = GEvent('click', + 'function() { location.href = "http://www.google.com"}') + polyline.add_event(event) + return render_to_response('mytemplate.html', + {'google' : GoogleMap(polylines=[polyline])}) + """ + + def __init__(self, event, action): + """ + Initializes a GEvent object. + + Parameters: + + event: + string for the event, such as 'click'. The event must be a valid + event for the object in the Google Maps API. + There is no validation of the event type within Django. + + action: + string containing a Javascript function, such as + 'function() { location.href = "newurl";}' + The string must be a valid Javascript function. Again there is no + validation fo the function within Django. + """ + self.event = event + self.action = action + + def __str__(self): + "Returns the parameter part of a GEvent." + return mark_safe('"%s", %s' %(self.event, self.action)) + +@python_2_unicode_compatible +class GOverlayBase(object): + def __init__(self): + self.events = [] + + def latlng_from_coords(self, coords): + "Generates a JavaScript array of GLatLng objects for the given coordinates." + return '[%s]' % ','.join(['new GLatLng(%s,%s)' % (y, x) for x, y in coords]) + + def add_event(self, event): + "Attaches a GEvent to the overlay object." + self.events.append(event) + + def __str__(self): + "The string representation is the JavaScript API call." + return mark_safe('%s(%s)' % (self.__class__.__name__, self.js_params)) + +class GPolygon(GOverlayBase): + """ + A Python wrapper for the Google GPolygon object. For more information + please see the Google Maps API Reference: + http://code.google.com/apis/maps/documentation/reference.html#GPolygon + """ + def __init__(self, poly, + stroke_color='#0000ff', stroke_weight=2, stroke_opacity=1, + fill_color='#0000ff', fill_opacity=0.4): + """ + The GPolygon object initializes on a GEOS Polygon or a parameter that + may be instantiated into GEOS Polygon. Please note that this will not + depict a Polygon's internal rings. + + Keyword Options: + + stroke_color: + The color of the polygon outline. Defaults to '#0000ff' (blue). + + stroke_weight: + The width of the polygon outline, in pixels. Defaults to 2. + + stroke_opacity: + The opacity of the polygon outline, between 0 and 1. Defaults to 1. + + fill_color: + The color of the polygon fill. Defaults to '#0000ff' (blue). + + fill_opacity: + The opacity of the polygon fill. Defaults to 0.4. + """ + if isinstance(poly, six.string_types): poly = fromstr(poly) + if isinstance(poly, (tuple, list)): poly = Polygon(poly) + if not isinstance(poly, Polygon): + raise TypeError('GPolygon may only initialize on GEOS Polygons.') + + # Getting the envelope of the input polygon (used for automatically + # determining the zoom level). + self.envelope = poly.envelope + + # Translating the coordinates into a JavaScript array of + # Google `GLatLng` objects. + self.points = self.latlng_from_coords(poly.shell.coords) + + # Stroke settings. + self.stroke_color, self.stroke_opacity, self.stroke_weight = stroke_color, stroke_opacity, stroke_weight + + # Fill settings. + self.fill_color, self.fill_opacity = fill_color, fill_opacity + + super(GPolygon, self).__init__() + + @property + def js_params(self): + return '%s, "%s", %s, %s, "%s", %s' % (self.points, self.stroke_color, self.stroke_weight, self.stroke_opacity, + self.fill_color, self.fill_opacity) + +class GPolyline(GOverlayBase): + """ + A Python wrapper for the Google GPolyline object. For more information + please see the Google Maps API Reference: + http://code.google.com/apis/maps/documentation/reference.html#GPolyline + """ + def __init__(self, geom, color='#0000ff', weight=2, opacity=1): + """ + The GPolyline object may be initialized on GEOS LineStirng, LinearRing, + and Polygon objects (internal rings not supported) or a parameter that + may instantiated into one of the above geometries. + + Keyword Options: + + color: + The color to use for the polyline. Defaults to '#0000ff' (blue). + + weight: + The width of the polyline, in pixels. Defaults to 2. + + opacity: + The opacity of the polyline, between 0 and 1. Defaults to 1. + """ + # If a GEOS geometry isn't passed in, try to contsruct one. + if isinstance(geom, six.string_types): geom = fromstr(geom) + if isinstance(geom, (tuple, list)): geom = Polygon(geom) + # Generating the lat/lng coordinate pairs. + if isinstance(geom, (LineString, LinearRing)): + self.latlngs = self.latlng_from_coords(geom.coords) + elif isinstance(geom, Polygon): + self.latlngs = self.latlng_from_coords(geom.shell.coords) + else: + raise TypeError('GPolyline may only initialize on GEOS LineString, LinearRing, and/or Polygon geometries.') + + # Getting the envelope for automatic zoom determination. + self.envelope = geom.envelope + self.color, self.weight, self.opacity = color, weight, opacity + super(GPolyline, self).__init__() + + @property + def js_params(self): + return '%s, "%s", %s, %s' % (self.latlngs, self.color, self.weight, self.opacity) + + +@total_ordering +class GIcon(object): + """ + Creates a GIcon object to pass into a Gmarker object. + + The keyword arguments map to instance attributes of the same name. These, + in turn, correspond to a subset of the attributes of the official GIcon + javascript object: + + http://code.google.com/apis/maps/documentation/reference.html#GIcon + + Because a Google map often uses several different icons, a name field has + been added to the required arguments. + + Required Arguments: + varname: + A string which will become the basis for the js variable name of + the marker, for this reason, your code should assign a unique + name for each GIcon you instantiate, otherwise there will be + name space collisions in your javascript. + + Keyword Options: + image: + The url of the image to be used as the icon on the map defaults + to 'G_DEFAULT_ICON' + + iconsize: + a tuple representing the pixel size of the foreground (not the + shadow) image of the icon, in the format: (width, height) ex.: + + GIcon('fast_food', + image="/media/icon/star.png", + iconsize=(15,10)) + + Would indicate your custom icon was 15px wide and 10px height. + + shadow: + the url of the image of the icon's shadow + + shadowsize: + a tuple representing the pixel size of the shadow image, format is + the same as ``iconsize`` + + iconanchor: + a tuple representing the pixel coordinate relative to the top left + corner of the icon image at which this icon is anchored to the map. + In (x, y) format. x increases to the right in the Google Maps + coordinate system and y increases downwards in the Google Maps + coordinate system.) + + infowindowanchor: + The pixel coordinate relative to the top left corner of the icon + image at which the info window is anchored to this icon. + + """ + def __init__(self, varname, image=None, iconsize=None, + shadow=None, shadowsize=None, iconanchor=None, + infowindowanchor=None): + self.varname = varname + self.image = image + self.iconsize = iconsize + self.shadow = shadow + self.shadowsize = shadowsize + self.iconanchor = iconanchor + self.infowindowanchor = infowindowanchor + + def __eq__(self, other): + return self.varname == other.varname + + def __lt__(self, other): + return self.varname < other.varname + + def __hash__(self): + # XOR with hash of GIcon type so that hash('varname') won't + # equal hash(GIcon('varname')). + return hash(self.__class__) ^ hash(self.varname) + +class GMarker(GOverlayBase): + """ + A Python wrapper for the Google GMarker object. For more information + please see the Google Maps API Reference: + http://code.google.com/apis/maps/documentation/reference.html#GMarker + + Example: + + from django.shortcuts import render_to_response + from django.contrib.gis.maps.google.overlays import GMarker, GEvent + + def sample_request(request): + marker = GMarker('POINT(101 26)') + event = GEvent('click', + 'function() { location.href = "http://www.google.com"}') + marker.add_event(event) + return render_to_response('mytemplate.html', + {'google' : GoogleMap(markers=[marker])}) + """ + def __init__(self, geom, title=None, draggable=False, icon=None): + """ + The GMarker object may initialize on GEOS Points or a parameter + that may be instantiated into a GEOS point. Keyword options map to + GMarkerOptions -- so far only the title option is supported. + + Keyword Options: + title: + Title option for GMarker, will be displayed as a tooltip. + + draggable: + Draggable option for GMarker, disabled by default. + """ + # If a GEOS geometry isn't passed in, try to construct one. + if isinstance(geom, six.string_types): geom = fromstr(geom) + if isinstance(geom, (tuple, list)): geom = Point(geom) + if isinstance(geom, Point): + self.latlng = self.latlng_from_coords(geom.coords) + else: + raise TypeError('GMarker may only initialize on GEOS Point geometry.') + # Getting the envelope for automatic zoom determination. + self.envelope = geom.envelope + # TODO: Add support for more GMarkerOptions + self.title = title + self.draggable = draggable + self.icon = icon + super(GMarker, self).__init__() + + def latlng_from_coords(self, coords): + return 'new GLatLng(%s,%s)' %(coords[1], coords[0]) + + def options(self): + result = [] + if self.title: result.append('title: "%s"' % self.title) + if self.icon: result.append('icon: %s' % self.icon.varname) + if self.draggable: result.append('draggable: true') + return '{%s}' % ','.join(result) + + @property + def js_params(self): + return '%s, %s' % (self.latlng, self.options()) diff --git a/lib/python2.7/site-packages/django/contrib/gis/maps/google/zoom.py b/lib/python2.7/site-packages/django/contrib/gis/maps/google/zoom.py new file mode 100644 index 0000000..c93cf4e --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/maps/google/zoom.py @@ -0,0 +1,162 @@ +from django.contrib.gis.geos import GEOSGeometry, LinearRing, Polygon, Point +from django.contrib.gis.maps.google.gmap import GoogleMapException +from django.utils.six.moves import xrange +from math import pi, sin, log, exp, atan + +# Constants used for degree to radian conversion, and vice-versa. +DTOR = pi / 180. +RTOD = 180. / pi + +class GoogleZoom(object): + """ + GoogleZoom is a utility for performing operations related to the zoom + levels on Google Maps. + + This class is inspired by the OpenStreetMap Mapnik tile generation routine + `generate_tiles.py`, and the article "How Big Is the World" (Hack #16) in + "Google Maps Hacks" by Rich Gibson and Schuyler Erle. + + `generate_tiles.py` may be found at: + http://trac.openstreetmap.org/browser/applications/rendering/mapnik/generate_tiles.py + + "Google Maps Hacks" may be found at http://safari.oreilly.com/0596101619 + """ + + def __init__(self, num_zoom=19, tilesize=256): + "Initializes the Google Zoom object." + # Google's tilesize is 256x256, square tiles are assumed. + self._tilesize = tilesize + + # The number of zoom levels + self._nzoom = num_zoom + + # Initializing arrays to hold the parameters for each one of the + # zoom levels. + self._degpp = [] # Degrees per pixel + self._radpp = [] # Radians per pixel + self._npix = [] # 1/2 the number of pixels for a tile at the given zoom level + + # Incrementing through the zoom levels and populating the parameter arrays. + z = tilesize # The number of pixels per zoom level. + for i in xrange(num_zoom): + # Getting the degrees and radians per pixel, and the 1/2 the number of + # for every zoom level. + self._degpp.append(z / 360.) # degrees per pixel + self._radpp.append(z / (2 * pi)) # radians per pixel + self._npix.append(z / 2) # number of pixels to center of tile + + # Multiplying `z` by 2 for the next iteration. + z *= 2 + + def __len__(self): + "Returns the number of zoom levels." + return self._nzoom + + def get_lon_lat(self, lonlat): + "Unpacks longitude, latitude from GEOS Points and 2-tuples." + if isinstance(lonlat, Point): + lon, lat = lonlat.coords + else: + lon, lat = lonlat + return lon, lat + + def lonlat_to_pixel(self, lonlat, zoom): + "Converts a longitude, latitude coordinate pair for the given zoom level." + # Setting up, unpacking the longitude, latitude values and getting the + # number of pixels for the given zoom level. + lon, lat = self.get_lon_lat(lonlat) + npix = self._npix[zoom] + + # Calculating the pixel x coordinate by multiplying the longitude value + # with with the number of degrees/pixel at the given zoom level. + px_x = round(npix + (lon * self._degpp[zoom])) + + # Creating the factor, and ensuring that 1 or -1 is not passed in as the + # base to the logarithm. Here's why: + # if fac = -1, we'll get log(0) which is undefined; + # if fac = 1, our logarithm base will be divided by 0, also undefined. + fac = min(max(sin(DTOR * lat), -0.9999), 0.9999) + + # Calculating the pixel y coordinate. + px_y = round(npix + (0.5 * log((1 + fac)/(1 - fac)) * (-1.0 * self._radpp[zoom]))) + + # Returning the pixel x, y to the caller of the function. + return (px_x, px_y) + + def pixel_to_lonlat(self, px, zoom): + "Converts a pixel to a longitude, latitude pair at the given zoom level." + if len(px) != 2: + raise TypeError('Pixel should be a sequence of two elements.') + + # Getting the number of pixels for the given zoom level. + npix = self._npix[zoom] + + # Calculating the longitude value, using the degrees per pixel. + lon = (px[0] - npix) / self._degpp[zoom] + + # Calculating the latitude value. + lat = RTOD * ( 2 * atan(exp((px[1] - npix)/ (-1.0 * self._radpp[zoom]))) - 0.5 * pi) + + # Returning the longitude, latitude coordinate pair. + return (lon, lat) + + def tile(self, lonlat, zoom): + """ + Returns a Polygon corresponding to the region represented by a fictional + Google Tile for the given longitude/latitude pair and zoom level. This + tile is used to determine the size of a tile at the given point. + """ + # The given lonlat is the center of the tile. + delta = self._tilesize / 2 + + # Getting the pixel coordinates corresponding to the + # the longitude/latitude. + px = self.lonlat_to_pixel(lonlat, zoom) + + # Getting the lower-left and upper-right lat/lon coordinates + # for the bounding box of the tile. + ll = self.pixel_to_lonlat((px[0]-delta, px[1]-delta), zoom) + ur = self.pixel_to_lonlat((px[0]+delta, px[1]+delta), zoom) + + # Constructing the Polygon, representing the tile and returning. + return Polygon(LinearRing(ll, (ll[0], ur[1]), ur, (ur[0], ll[1]), ll), srid=4326) + + def get_zoom(self, geom): + "Returns the optimal Zoom level for the given geometry." + # Checking the input type. + if not isinstance(geom, GEOSGeometry) or geom.srid != 4326: + raise TypeError('get_zoom() expects a GEOS Geometry with an SRID of 4326.') + + # Getting the envelope for the geometry, and its associated width, height + # and centroid. + env = geom.envelope + env_w, env_h = self.get_width_height(env.extent) + center = env.centroid + + for z in xrange(self._nzoom): + # Getting the tile at the zoom level. + tile_w, tile_h = self.get_width_height(self.tile(center, z).extent) + + # When we span more than one tile, this is an approximately good + # zoom level. + if (env_w > tile_w) or (env_h > tile_h): + if z == 0: + raise GoogleMapException('Geometry width and height should not exceed that of the Earth.') + return z-1 + + # Otherwise, we've zoomed in to the max. + return self._nzoom-1 + + def get_width_height(self, extent): + """ + Returns the width and height for the given extent. + """ + # Getting the lower-left, upper-left, and upper-right + # coordinates from the extent. + ll = Point(extent[:2]) + ul = Point(extent[0], extent[3]) + ur = Point(extent[2:]) + # Calculating the width and height. + height = ll.distance(ul) + width = ul.distance(ur) + return width, height |