web/lib/django/contrib/gis/maps/google/gmap.py
changeset 0 0d40e90630ef
equal deleted inserted replaced
-1:000000000000 0:0d40e90630ef
       
     1 from django.conf import settings
       
     2 from django.contrib.gis import geos
       
     3 from django.template.loader import render_to_string
       
     4 from django.utils.safestring import mark_safe
       
     5 
       
     6 class GoogleMapException(Exception): pass
       
     7 from django.contrib.gis.maps.google.overlays import GPolygon, GPolyline, GMarker, GIcon
       
     8 
       
     9 # The default Google Maps URL (for the API javascript)
       
    10 # TODO: Internationalize for Japan, UK, etc.
       
    11 GOOGLE_MAPS_URL='http://maps.google.com/maps?file=api&v=%s&key='
       
    12 
       
    13 class GoogleMap(object):
       
    14     "A class for generating Google Maps JavaScript."
       
    15 
       
    16     # String constants
       
    17     onunload = mark_safe('onunload="GUnload()"') # Cleans up after Google Maps
       
    18     vml_css  = mark_safe('v\:* {behavior:url(#default#VML);}') # CSS for IE VML
       
    19     xmlns    = mark_safe('xmlns:v="urn:schemas-microsoft-com:vml"') # XML Namespace (for IE VML).
       
    20 
       
    21     def __init__(self, key=None, api_url=None, version=None,
       
    22                  center=None, zoom=None, dom_id='map',
       
    23                  kml_urls=[], polylines=None, polygons=None, markers=None,
       
    24                  template='gis/google/google-map.js',
       
    25                  js_module='geodjango',
       
    26                  extra_context={}):
       
    27 
       
    28         # The Google Maps API Key defined in the settings will be used
       
    29         # if not passed in as a parameter.  The use of an API key is
       
    30         # _required_.
       
    31         if not key:
       
    32             try:
       
    33                 self.key = settings.GOOGLE_MAPS_API_KEY
       
    34             except AttributeError:
       
    35                 raise GoogleMapException('Google Maps API Key not found (try adding GOOGLE_MAPS_API_KEY to your settings).')
       
    36         else:
       
    37             self.key = key
       
    38 
       
    39         # Getting the Google Maps API version, defaults to using the latest ("2.x"),
       
    40         # this is not necessarily the most stable.
       
    41         if not version:
       
    42             self.version = getattr(settings, 'GOOGLE_MAPS_API_VERSION', '2.x')
       
    43         else:
       
    44             self.version = version
       
    45 
       
    46         # Can specify the API URL in the `api_url` keyword.
       
    47         if not api_url:
       
    48             self.api_url = mark_safe(getattr(settings, 'GOOGLE_MAPS_URL', GOOGLE_MAPS_URL) % self.version)
       
    49         else:
       
    50             self.api_url = api_url
       
    51 
       
    52         # Setting the DOM id of the map, the load function, the JavaScript
       
    53         # template, and the KML URLs array.
       
    54         self.dom_id = dom_id
       
    55         self.extra_context = extra_context
       
    56         self.js_module = js_module
       
    57         self.template = template
       
    58         self.kml_urls = kml_urls
       
    59 
       
    60         # Does the user want any GMarker, GPolygon, and/or GPolyline overlays?
       
    61         overlay_info = [[GMarker, markers, 'markers'],
       
    62                         [GPolygon, polygons, 'polygons'],
       
    63                         [GPolyline, polylines, 'polylines']]
       
    64 
       
    65         for overlay_class, overlay_list, varname in overlay_info:
       
    66             setattr(self, varname, [])
       
    67             if overlay_list:
       
    68                 for overlay in overlay_list:
       
    69                     if isinstance(overlay, overlay_class):
       
    70                         getattr(self, varname).append(overlay)
       
    71                     else:
       
    72                         getattr(self, varname).append(overlay_class(overlay))
       
    73 
       
    74         # If GMarker, GPolygons, and/or GPolylines are used the zoom will be
       
    75         # automatically calculated via the Google Maps API.  If both a zoom
       
    76         # level and a center coordinate are provided with polygons/polylines,
       
    77         # no automatic determination will occur.
       
    78         self.calc_zoom = False
       
    79         if self.polygons or self.polylines  or self.markers:
       
    80             if center is None or zoom is None:
       
    81                 self.calc_zoom = True
       
    82 
       
    83         # Defaults for the zoom level and center coordinates if the zoom
       
    84         # is not automatically calculated.
       
    85         if zoom is None: zoom = 4
       
    86         self.zoom = zoom
       
    87         if center is None: center = (0, 0)
       
    88         self.center = center
       
    89 
       
    90     def render(self):
       
    91         """
       
    92         Generates the JavaScript necessary for displaying this Google Map.
       
    93         """
       
    94         params = {'calc_zoom' : self.calc_zoom,
       
    95                   'center' : self.center,
       
    96                   'dom_id' : self.dom_id,
       
    97                   'js_module' : self.js_module,
       
    98                   'kml_urls' : self.kml_urls,
       
    99                   'zoom' : self.zoom,
       
   100                   'polygons' : self.polygons,
       
   101                   'polylines' : self.polylines,
       
   102                   'icons': self.icons,
       
   103                   'markers' : self.markers,
       
   104                   }
       
   105         params.update(self.extra_context)
       
   106         return render_to_string(self.template, params)
       
   107 
       
   108     @property
       
   109     def body(self):
       
   110         "Returns HTML body tag for loading and unloading Google Maps javascript."
       
   111         return mark_safe('<body %s %s>' % (self.onload, self.onunload))
       
   112 
       
   113     @property
       
   114     def onload(self):
       
   115         "Returns the `onload` HTML <body> attribute."
       
   116         return mark_safe('onload="%s.%s_load()"' % (self.js_module, self.dom_id))
       
   117 
       
   118     @property
       
   119     def api_script(self):
       
   120         "Returns the <script> tag for the Google Maps API javascript."
       
   121         return mark_safe('<script src="%s%s" type="text/javascript"></script>' % (self.api_url, self.key))
       
   122 
       
   123     @property
       
   124     def js(self):
       
   125         "Returns only the generated Google Maps JavaScript (no <script> tags)."
       
   126         return self.render()
       
   127 
       
   128     @property
       
   129     def scripts(self):
       
   130         "Returns all <script></script> tags required with Google Maps JavaScript."
       
   131         return mark_safe('%s\n  <script type="text/javascript">\n//<![CDATA[\n%s//]]>\n  </script>' % (self.api_script, self.js))
       
   132 
       
   133     @property
       
   134     def style(self):
       
   135         "Returns additional CSS styling needed for Google Maps on IE."
       
   136         return mark_safe('<style type="text/css">%s</style>' % self.vml_css)
       
   137 
       
   138     @property
       
   139     def xhtml(self):
       
   140         "Returns XHTML information needed for IE VML overlays."
       
   141         return mark_safe('<html xmlns="http://www.w3.org/1999/xhtml" %s>' % self.xmlns)
       
   142 
       
   143     @property
       
   144     def icons(self):
       
   145         "Returns a sequence of GIcon objects in this map."
       
   146         return set([marker.icon for marker in self.markers if marker.icon])
       
   147 
       
   148 class GoogleMapSet(GoogleMap):
       
   149 
       
   150     def __init__(self, *args, **kwargs):
       
   151         """
       
   152         A class for generating sets of Google Maps that will be shown on the
       
   153         same page together.
       
   154 
       
   155         Example:
       
   156          gmapset = GoogleMapSet( GoogleMap( ... ), GoogleMap( ... ) )
       
   157          gmapset = GoogleMapSet( [ gmap1, gmap2] )
       
   158         """
       
   159         # The `google-multi.js` template is used instead of `google-single.js`
       
   160         # by default.
       
   161         template = kwargs.pop('template', 'gis/google/google-multi.js')
       
   162 
       
   163         # This is the template used to generate the GMap load JavaScript for
       
   164         # each map in the set.
       
   165         self.map_template = kwargs.pop('map_template', 'gis/google/google-single.js')
       
   166 
       
   167         # Running GoogleMap.__init__(), and resetting the template
       
   168         # value with default obtained above.
       
   169         super(GoogleMapSet, self).__init__(**kwargs)
       
   170         self.template = template
       
   171 
       
   172         # If a tuple/list passed in as first element of args, then assume
       
   173         if isinstance(args[0], (tuple, list)):
       
   174             self.maps = args[0]
       
   175         else:
       
   176             self.maps = args
       
   177 
       
   178         # Generating DOM ids for each of the maps in the set.
       
   179         self.dom_ids = ['map%d' % i for i in xrange(len(self.maps))]
       
   180 
       
   181     def load_map_js(self):
       
   182         """
       
   183         Returns JavaScript containing all of the loading routines for each
       
   184         map in this set.
       
   185         """
       
   186         result = []
       
   187         for dom_id, gmap in zip(self.dom_ids, self.maps):
       
   188             # Backup copies the GoogleMap DOM id and template attributes.
       
   189             # They are overridden on each GoogleMap instance in the set so
       
   190             # that only the loading JavaScript (and not the header variables)
       
   191             # is used with the generated DOM ids.
       
   192             tmp = (gmap.template, gmap.dom_id)
       
   193             gmap.template = self.map_template
       
   194             gmap.dom_id = dom_id
       
   195             result.append(gmap.js)
       
   196             # Restoring the backup values.
       
   197             gmap.template, gmap.dom_id = tmp
       
   198         return mark_safe(''.join(result))
       
   199 
       
   200     def render(self):
       
   201         """
       
   202         Generates the JavaScript for the collection of Google Maps in
       
   203         this set.
       
   204         """
       
   205         params = {'js_module' : self.js_module,
       
   206                   'dom_ids' : self.dom_ids,
       
   207                   'load_map_js' : self.load_map_js(),
       
   208                   'icons' : self.icons,
       
   209                   }
       
   210         params.update(self.extra_context)
       
   211         return render_to_string(self.template, params)
       
   212 
       
   213     @property
       
   214     def onload(self):
       
   215         "Returns the `onload` HTML <body> attribute."
       
   216         # Overloaded to use the `load` function defined in the
       
   217         # `google-multi.js`, which calls the load routines for
       
   218         # each one of the individual maps in the set.
       
   219         return mark_safe('onload="%s.load()"' % self.js_module)
       
   220 
       
   221     @property
       
   222     def icons(self):
       
   223         "Returns a sequence of all icons in each map of the set."
       
   224         icons = set()
       
   225         for map in self.maps: icons |= map.icons
       
   226         return icons