source: googlemapmacro/0.11/tracgooglemap/macro.py

Last change on this file was 17139, checked in by Ryan J Ollos, 5 years ago

TracGoogleMapMacro 0.6: Conform to PEP8

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Id URL Author Date Rev
File size: 25.9 KB
Line 
1# -*- coding: utf-8 -*-
2""" Copyright (c) 2008-2010 Martin Scharrer <martin@scharrer-online.de>
3    $Id: macro.py 17139 2018-04-16 20:14:38Z rjollos $
4    $HeadURL: //trac-hacks.org/svn/googlemapmacro/0.11/tracgooglemap/macro.py $
5
6    This is Free Software under the GPL v3!
7"""
8
9import re
10from urllib import urlopen, quote_plus
11
12from trac.config import Option, IntOption
13from trac.core import TracError, implements
14from trac.env import IEnvironmentSetupParticipant
15from trac.util import md5
16from trac.util.html import Element, Markup, html as tag
17from trac.wiki.formatter import extract_link
18from trac.wiki.macros import WikiMacroBase
19from trac.web.api import IRequestFilter
20from trac.web.chrome import (
21    ITemplateProvider, add_script, add_link, add_stylesheet)
22
23from tracadvparseargs import parse_args
24
25COUNT = '_googlemapmacro_count'
26
27_reWHITESPACES = re.compile(r'\s+')
28_reCOMMA = re.compile(r',\s*')
29_reCOORDS = re.compile(r'^[+-]?\d+(?:\.\d*)?[:,][+-]?\d+(?:\.\d*)?$')
30_reDBLQUOTE = re.compile(r'(?<!\\)"')
31
32_default_map_types = ['NORMAL', 'SATELLITE', 'HYBRID']
33_supported_map_types = ['NORMAL', 'SATELLITE', 'HYBRID', 'PHYSICAL']
34_supported_controls = {}
35for control in ('LargeMap', 'SmallMap', 'SmallZoom', 'Scale',
36                'MapType', 'HierarchicalMapType', 'OverviewMap'):
37    _supported_controls[control.upper()] = control
38
39_css_units = ('em', 'ex', 'px', 'in', 'cm', 'mm', 'pt', 'pc')
40
41_accuracy_to_zoom = (3, 4, 8, 10, 12, 14, 14, 15, 16, 16)
42
43_javascript_code = """
44//<![CDATA[
45
46TracGoogleMap( function (mapdiv,index) {
47    var map = new GMap2(mapdiv, {
48    //    size: new GSize(%(width)s, %(height)s),
49        mapTypes: %(types_str)s
50    });
51    %(controls_str)s
52    map.setMapType(G_%(type)s_MAP);
53    if ("%(center)s") {
54        map.setCenter(new GLatLng(%(center)s), %(zoom)s);
55    }
56    var geocoder = new GClientGeocoder();
57    var address  = "%(address)s";
58    if (address) {
59        geocoder.getLatLng(
60            address,
61            function(point) {
62                if (point) {
63                    map.setCenter(point, %(zoom)s);
64                }
65            }
66        )
67    }
68    %(markers_str)s
69    if ("%(directions)s") {
70        dirdiv = document.getElementById("tracgooglemap-directions-" + index);
71        gdir = new GDirections(map, dirdiv);
72        gdir.load("%(directions)s");
73    }
74}, "%(id)s" );
75
76//]]>
77"""
78
79
80class GoogleMapMacro(WikiMacroBase):
81    """ Provides a macro to insert Google Maps(TM) in Wiki pages.
82
83== Description ==
84
85Website: http://trac-hacks.org/wiki/GoogleMapMacro
86
87`$Id: macro.py 17139 2018-04-16 20:14:38Z rjollos $`
88
89This macro lets the user insert a full dynamic
90[http://maps.google.com/ Google Map]. Because a lot of javascript is used (by
91Google) a
92[http://local.google.com/support/bin/answer.py?answer=16532&amp;topic=1499 Google Map compatible browser]
93is needed. Newer version of Firefox and MS Internet Explorer are compatible.
94
95For javascript-less static maps use the similar
96[http://trac-hacks.org/wiki/GoogleStaticMapMacro GoogleStaticMapMacro].
97
98Multiple Google Maps on the same wiki page are actively supported.
99
100== Dependencies ==
101The recent version (r480) of this macro needs the AdvParseArgsPlugin in revision
1024795 or later.
103
104== Configuration ==
105
106A different [http://code.google.com/apis/maps/signup.html Google Map API key] is
107needed for every web domain which can be get for free from Google.
108'''Please check if the Google Map API Terms of Use apply for your Trac
109project.''' They do apply IMHO for non-pay open accessible Trac projects.
110
111== Usage ==
112The macro knows the following arguments, which can be used in the normal
113`key1=value1,key2=value2,...` syntax. If a value includes one or more comma then
114it must be set in double quotes (`" "`).
115If a key-less value is given it will be taken as `center` coordinates if it's in
116the proper format otherwise it's taken as an `address`. Unknown (or misspelled)
117keys or key-less values except the first are silently ignored.
118
119 `address`:: Sets the center of the map to the given address. The value must be
120             surrounded by quotes if it includes commas, e.g. `"Street, City,
121             Country"`.
122             If an `address` but no `zoom` value was given an appropriate value
123             will be guessed based on the address accuracy returned by Google.
124             At the moment this isn't very accurate but soon
125             [http://groups.google.com/group/Google-Maps-API/browse_thread/thread/53c4525e8d01e75d Google might improve] this.
126 `center`:: Sets the center of the map to the given coordinates. The format is `{latitude}:{longitude}` or `"{latitude},{longitude}"`.
127 `zoom`:: Sets zoom factor. Allowed values are between 0 (whole world) and 19 (single house). Very high zoom values might not be supported by Google Maps for all parts of the world.
128 `size`:: The size either in the format `{width}x{height}` as numbers in pixel, e.g.: `300x400` means 300px wide and 400px high or
129          in the format `{width}{unit}:{height}{unit}` where unit can be any CSS unit (em, ex, px, in, cm, mm, pt, pc).
130 `types` (optional):: Sets the map types selectable by the user, separated by colons ('`:`'). If not given the standard types of Google Maps are used (Normal, Satellite, Hybrid). The following types are available (values are case-insensitive):
131   * `normal` Normal street-map
132   * `satellite` Satellite picture
133   * `hybrid` Satellite picture with streets as overlay
134   * `physical` Terrain map
135 `type` (optional):: Sets the initial map type. See the `types` argument for the available types. If this argument is not given the first listed type under `types` is used initially.
136 `controls` (optional):: Sets the used map controls. Multiple controls can be given, separated by colon ('`:`'). The value is case-insensitive. If not set the controls `MapType` and `LargeMap` are used.
137 If set but empty no controls are displayed. The following controls are available (descriptions taken from the [http://code.google.com/apis/maps/documentation/reference.html#GControlImpl Google Map API page]):
138   * `LargeMap`: Control with buttons to pan in four directions, and zoom in and zoom out.
139   * `SmallMap`: Control with buttons to pan in four directions, and zoom in and zoom out, and a zoom slider.
140   * `SmallZoom`: Control with buttons to zoom in and zoom out.
141   * `Scale`: Control that displays the map scale.
142   * `MapType`: Standard map type control for selecting and switching between supported map types via buttons.
143   * `HierarchicalMapType`: Drop-down map type control for switching between supported map types.
144   * `OverviewMap`: Collapsible overview mini-map in the corner of the main map for reference location and navigation (through dragging).
145 `marker`:: (New) Allows the user to set labeled markers on given location of the map.
146    The format for a marker is `{latitude}:{longitude};{Letter};{TracLink};{Title}` or `"{Address}";{Letter};{TracLink};{Title}`.
147    If the string 'center' is used instead of an address the center of the map is marked.
148    The optional marker letter can be either A-Z or 'o', '.' or empty for a plain marker.
149    An optional [TracLinks TracLink] can be given which may be opened in a new window (see `target`) when clicked on the marker.
150    An optional marker title can be set which will get displayed when the mouse cursor is over the marker.
151    From revision [4801] on multiple markers can be given which replaces the `markers` argument.
152 `markers`:: (Old) Can be used to set multiple markers which are are separated using the '`|`' character.
153    Optional values can be kept empty, e.g.: `"{Address}";;;My Address` would display the address with the title 'My Address'.
154    Trailing semicolons can be skipped. Addresses, links and titles which include either '`,`', '`;`' or '`|`' must be enclosed in double quotes (`" "`).
155 `target`:: If set to '`new`' or '`newwindow`' all hyperlinks of the map markers will be opened in a new window (or tab, depending on the browser settings) or in the same window otherwise.
156 `from`,`to`:: Request driving directions '`from`'->'`to`'. Multiple `to` addresses are allowed. No `address` or `center` need to be given.
157
158== Examples ==
159
160=== Using geographic coordinates ===
161Please use a colon, not a comma, as separator for the coordinates.
162{{{
163[[GoogleMap(center=50.0:10.0,zoom=10,size=400x400)]]
164or
165[[GoogleMap("50.0:10.0",zoom=10,size=400x400)]]
166or
167[[GoogleMap(50.0:10.0,zoom=10,size=400x400)]]
168}}}
169=== Using an address ===
170Please use semicolons, not commas, as separators in the address.
171{{{
172[[GoogleMap(address="Street, City, Country",zoom=10,size=400x400)]]
173or
174[[GoogleMap("Street, City, Country",zoom=10,size=400x400)]]
175or, if you really want to:
176[[GoogleMap(Street; City; Country,zoom=10,size=400x400)]]
177}}}
178Please note that the address is converted into coordinates by user-side javascript every time the wiki page is loaded.
179If this fails no map will be shown, only an empty gray rectangle.
180
181=== Using both ===
182If both address and center coordinates are given, then the result depends on the [#config `geocoding` setting]:
183 `server`:: The address is resolved on the trac server and the coordinates are completely ignored.
184 `client`:: The map is first centered at the given coordinates and then moved to the given address after (and if) it was
185            resolved by the client-side !JavaScript code.
186{{{
187[[GoogleMap(center=50.0:10.0,address="Street, City, Country",zoom=10,size=400x400)]]
188}}}
189
190=== Select Map Types ===
191To show a map with the standard map types where the satellite map is preselected:
192{{{
193[[GoogleMap("Street, City, Country",zoom=10,size=400x400,type=satellite)]]
194}}}
195
196To only show a satellite map (please note the added '`s`'):
197{{{
198[[GoogleMap("Street, City, Country",zoom=10,size=400x400,types=satellite)]]
199}}}
200
201To show a map with hybrid and satellite map types (in this order) where the satellite map is preselected:
202{{{
203[[GoogleMap("Street, City, Country",zoom=10,size=400x400,types=hybrid:satellite,type=satellite)]]
204}}}
205
206To show a map with hybrid and satellite map types (in this order) where the hybrid map is preselected:
207{{{
208[[GoogleMap("Street, City, Country",zoom=10,size=400x400,types=hybrid:satellite)]]
209or
210[[GoogleMap("Street, City, Country",zoom=10,size=400x400,types=hybrid:satellite,type=hybrid)]]
211}}}
212
213=== Markers ===
214To create three markers: one at the center of the map (A), one at the next street (B) and one at coordinates 10.243,23.343 (C):
215{{{
216[[GoogleMap("Street, City, Country",zoom=10,size=400x400,markers=center;A|"Next street, City, Country";B|10.243:23.343;C)]]
217}}}
218The same with hyperlinked markers:
219{{{
220[[GoogleMap("Street, City, Country",zoom=10,size=400x400,markers=center;A;wiki:MyWikiPage|"Next street, City, Country";B;ticket:1|10.243:23.343;C;http://www.example.com/)]]
221}}}
222    """
223    implements(IRequestFilter, ITemplateProvider,
224               IRequestFilter, IEnvironmentSetupParticipant)
225
226    geocoding = Option(
227        'googlemap', 'geocoding', 'client',
228        """Which side is handling the geocoding: either "server" or
229        "client" (default).
230        """)
231
232    api_key = Option(
233        'googlemap', 'api_key', '',
234        """Google Map API key. Available from
235        http://code.google.com/apis/maps/signup.html .
236        """)
237
238    default_zoom = IntOption(
239        'googlemap', 'default_zoom', '6',
240        "Default map zoom used if no zoom specified by the user (default: 6)")
241
242    default_size = Option(
243        'googlemap', 'default_size', '300x300',
244        """Default map size (width x height, in pixel without units) used
245        if no size specified by the user (default: 300x300)
246        """)
247
248    default_target = Option(
249        'googlemap', 'default_target', '',
250        """Default target for hyperlinked markers. Use "_blank" to open
251        target in new window. (Default: "")
252        """)
253
254    def __init__(self):
255        self.geocoding_server = self.geocoding.lower() == "server"
256
257    def _create_db_table(self, db=None, commit=True):
258        """ Create DB table if it not exists. """
259        if self.geocoding_server:
260            self.log.debug("Creating DB table (if not already exists).")
261
262            db = db or self.env.get_db_cnx()
263            cursor = db.cursor()
264            cursor.execute("""
265                CREATE TABLE IF NOT EXISTS googlemapmacro (
266                    id char(32) Unique,
267                    lon decimal(10,6),
268                    lat decimal(10,6),
269                    acc decimal(2,0)
270                );""")
271            if commit:
272                db.commit()
273        return
274
275    def environment_created(self):
276        self._create_db_table()
277        return
278
279    def environment_needs_upgrade(self, db):
280        if not self.geocoding_server:
281            return False
282        cursor = db.cursor()
283        try:
284            cursor.execute("SELECT count(*) FROM googlemapmacro;")
285            cursor.fetchone()
286        except:
287            return True
288        return False
289
290    def upgrade_environment(self, db):
291        self._create_db_table(db, False)
292        return
293
294    # ITemplateProvider#get_htdocs_dirs
295
296    def get_htdocs_dirs(self):
297        from pkg_resources import resource_filename
298        return [('googlemap', resource_filename(__name__, 'htdocs'))]
299
300    # ITemplateProvider#get_templates_dirs
301    def get_templates_dirs(self):
302        return []
303
304    # IRequestFilter#pre_process_request
305
306    def pre_process_request(self, req, handler):
307        return handler
308
309    # IRequestFilter#post_process_request
310
311    def post_process_request(self, req, template, data, content_type):
312        # Add Google Map API key using a link tag:
313        if self.api_key:
314            add_link(req, rel='google-key', href='',
315                     title=self.api_key, classname='google-key')
316            add_stylesheet(req, 'googlemap/tracgooglemap.css')
317            add_script(req, 'googlemap/tracgooglemap.js')
318        return (template, data, content_type)
319
320    def _strip(self, arg):
321        """Strips spaces and a single pair of double quotes as long there are
322           no unescaped double quotes in the middle.  """
323        arg = unicode(arg).strip()
324        if len(arg) < 2:
325            return arg
326        if arg.startswith('"') and arg.endswith('"') \
327           and not _reDBLQUOTE.match(arg[1:-1]):
328            arg = arg[1:-1]
329        return arg
330
331    def _format_address(self, address):
332        address = self._strip(address).replace(';', ',')
333        address = _reWHITESPACES.sub(' ', address)
334        address = _reCOMMA.sub(', ', address)
335        return address
336
337    def _get_coords(self, address):
338        m = md5()
339        m.update(address)
340        hash = m.hexdigest()
341
342        db = self.env.get_db_cnx()
343        cursor = db.cursor()
344        cursor.execute(
345            "SELECT lon,lat,acc FROM googlemapmacro WHERE id='%s';" % hash)
346        for row in cursor:
347            if len(row) == 3:
348                self.log.debug("Reusing coordinates from database")
349                return (str(row[0]), str(row[1]), str(row[2]))
350
351        response = None
352        url = r'http://maps.google.com/maps/geo?output=csv&q=' + \
353            quote_plus(address)
354        try:
355            response = urlopen(url).read()
356        except:
357            raise TracError(
358                "Google Maps could not be contacted to resolve address!")
359        self.log.debug("Google geocoding response: '%s'", response)
360        resp = response.split(',')
361        if len(resp) != 4 or not resp[0] == "200":
362            raise TracError(
363                "Given address '%s' couldn't be resolved by Google Maps!"
364                % address)
365        acc, lon, lat = resp[1:4]
366
367        cursor.execute("""
368            INSERT INTO googlemapmacro (id, lon, lat, acc)
369            VALUES ('%s', %s, %s, %s);
370            """, (hash, lon, lat, acc))
371        db.commit()
372        self.env.log.debug("Saving coordinates to database")
373
374        return (lon, lat, acc)
375
376    def expand_macro(self, formatter, name, content, args=None):
377        content = content.replace('\n', ',')
378        largs, kwargs = parse_args(content, multi=['marker', 'to'])
379        if len(largs) > 0:
380            arg = unicode(largs[0])
381            if _reCOORDS.match(arg):
382                if 'center' not in kwargs:
383                    kwargs['center'] = arg
384            else:
385                if 'address' not in kwargs:
386                    kwargs['address'] = arg
387        if 'from' in kwargs and 'address' not in kwargs and \
388                'center' not in kwargs:
389            arg = unicode(kwargs['from'])
390            if _reCOORDS.match(arg):
391                if 'center' not in kwargs:
392                    kwargs['center'] = arg
393            else:
394                if 'address' not in kwargs:
395                    kwargs['address'] = arg
396
397        # Check if Google API key is set (if not the Google Map script file
398        # wasn't inserted by `post_process_request` and the map wont load)
399        if not self.api_key:
400            raise TracError(
401                "No Google Maps API key given! Tell your web admin to "
402                "get one at http://code.google.com/apis/maps/signup.html.\n")
403
404        # Use default values if needed
405        zoom = None
406        size = None
407        try:
408            if 'zoom' in kwargs:
409                zoom = unicode(int(kwargs['zoom']))
410            else:
411                zoom = unicode(self.default_zoom)
412        except:
413            raise TracError(
414                "Invalid value for zoom given! Please provide an integer "
415                "from 0 to 19.")
416
417        if 'size' in kwargs:
418            size = unicode(kwargs['size'])
419        else:
420            size = unicode(self.default_size)
421
422        # Set target for hyperlinked markers
423        target = ""
424        if 'target' not in kwargs:
425            kwargs['target'] = self.default_target
426        if kwargs['target'] in ('new', 'newwindow', '_blank'):
427            target = "newwindow"
428
429        # Get height and width
430        width = None
431        height = None
432        try:
433            if size.find(':') != -1:
434                (width, height) = size.lower().split(':')
435                # Check for correct units:
436                if not width[-2:] in _css_units \
437                   or not height[-2:] in _css_units:
438                    raise TracError("Wrong unit(s)!")
439                # The rest must be a number:
440                float(width[:-2])
441                float(height[:-2])
442            else:
443                (width, height) = size.lower().split('x')
444                width = str(int(width)) + "px"
445                height = str(int(height)) + "px"
446        except:
447            raise TracError("Invalid value for size given! Please provide "
448                            "{width}x{height} in pixels (without unit) or "
449                            "{width}{unit}:{height}{unit} in CSS units (%s)."
450                            % ', '.join(_css_units))
451
452        # Correct separator for 'center' argument because comma isn't
453        # allowed in macro arguments
454        center = ""
455        if 'center' in kwargs:
456            center = unicode(kwargs['center']).replace(':', ',').strip(' "\'')
457            if not _reCOORDS.match(center):
458                raise TracError("Invalid center coordinates given!")
459
460        # Format address
461        address = ""
462        if 'address' in kwargs:
463            address = self._format_address(kwargs['address'])
464            if self.geocoding_server:
465                coord = self._get_coords(address)
466                center = ",".join(coord[0:2])
467                address = ""
468                if 'zoom' not in kwargs:
469                    zoom = _accuracy_to_zoom[int(coord[2])]
470
471        # Internal formatting functions:
472        def gtyp(stype):
473            return "G_%s_MAP" % str(stype)
474
475        def gcontrol(control):
476            return "map.addControl(new G%sControl());\n" % str(control)
477
478        def gmarker(lat, lng, letter='', link='', title=''):
479            if not title:
480                title = link
481            if not letter:
482                letter = ''
483            else:
484                letter = str(letter).upper()
485                if str(letter).startswith('.'):
486                    letter = ''
487                else:
488                    letter = letter[0]
489            return ("SetMarkerByCoords(map,%s,%s,'%s','%s','%s', '%s');\n"
490                    % (lat, lng, letter, link, title, target))
491
492        def gmarkeraddr(address, letter='', link='', title=''):
493            if not title:
494                title = link
495            if not letter:
496                letter = ''
497            else:
498                letter = str(letter).upper()
499                if str(letter).startswith('.'):
500                    letter = ''
501                else:
502                    letter = letter[0]
503            return (
504                "SetMarkerByAddress(map,'%s','%s','%s','%s','%s',geocoder);\n"
505                % (address, letter, link, title, target))
506
507        # Set initial map type
508        type = 'NORMAL'
509        types = []
510        types_str = None
511        if 'types' in kwargs:
512            types = unicode(kwargs['types']).upper().split(':')
513            types_str = ','.join(map(gtyp, types))
514            type = types[0]
515
516        if 'type' in kwargs:
517            type = unicode(kwargs['type']).upper()
518            if 'types' in kwargs and type not in types:
519                types_str += ',' + type
520                types.insert(0, type)
521            elif type not in _supported_map_types:
522                type = 'NORMAL'
523            # if types aren't set and a type is set which is supported
524            # but not a default type:
525            if 'types' not in kwargs and type in _supported_map_types and \
526                    type not in _default_map_types:
527                # enable type (and all default types):
528                types = _default_map_types + [type]
529                types_str = ','.join(map(gtyp, types))
530
531        if types_str:
532            types_str = '[' + types_str + ']'
533        else:
534            types_str = 'G_DEFAULT_MAP_TYPES'
535
536        # Produce controls
537        controls = ['LargeMap', 'MapType']
538        if 'controls' in kwargs:
539            controls = []
540            for control in unicode(kwargs['controls']).upper().split(':'):
541                if control in _supported_controls:
542                    controls.append(_supported_controls[control])
543        controls_str = ''.join(map(gcontrol, controls))
544
545        # Produce markers
546        markers_str = ""
547        if 'marker' not in kwargs:
548            kwargs['marker'] = []
549        if 'markers' in kwargs:
550            kwargs['marker'].extend(
551                parse_args(unicode(kwargs['markers']), delim='|',
552                           listonly=True))
553        if kwargs['marker']:
554            markers = []
555            for marker in kwargs['marker']:
556                location, letter, link, title = \
557                    parse_args(marker, delim=';', listonly=True, minlen=4)[:4]
558                if not title:
559                    title = link
560
561                # Convert wiki to HTML link:
562                link = extract_link(self.env, formatter.context, link)
563                if isinstance(link, Element):
564                    link = link.attrib.get('href')
565                else:
566                    link = ''
567
568                location = self._format_address(location)
569                if _reCOORDS.match(location):
570                    coord = location.split(':')
571                    markers.append(
572                        gmarker(coord[0], coord[1], letter, link, title))
573                else:
574                    if self.geocoding_server:
575                        coord = []
576                        if location == 'center':
577                            if address:
578                                coord = self._get_coords(address)
579                            else:
580                                coord = center.split(',')
581                        else:
582                            coord = self._get_coords(location)
583                        markers.append(
584                            gmarker(coord[0], coord[1], letter, link, title))
585                    else:
586                        if location == 'center':
587                            if address:
588                                markers.append(gmarkeraddr(
589                                    address, letter, link, title))
590                            else:
591                                coord = center.split(',')
592                                markers.append(
593                                    gmarker(coord[0], coord[1], letter,
594                                            link, title))
595                        else:
596                            markers.append(gmarkeraddr(
597                                location, letter, link, title))
598            markers_str = ''.join(markers)
599
600        # Get macro count from request object
601        req = formatter.req
602        count = getattr(req, COUNT, 0)
603        id = 'tracgooglemap-%s' % count
604        setattr(req, COUNT, count + 1)
605
606        # Canvas for this map
607        mapdiv = tag.div(
608            "Google Map is loading ... (JavaScript enabled?)",
609            id=id,
610            style="width: %s; height: %s;" % (width, height),
611            class_="tracgooglemap"
612        )
613
614        if 'from' in kwargs and 'to' in kwargs:
615            directions = "from: %s to: %s" % (
616                kwargs['from'], ' to: '.join(list(kwargs['to'])))
617            mapnmore = tag.table(
618                tag.tr(
619                    tag.td(
620                        tag.div("",
621                                class_='tracgooglemap-directions',
622                                id='tracgooglemap-directions-%s' % count
623                                ),
624                        style="vertical-align:top;",
625                    ),
626                    tag.td(
627                        mapdiv,
628                        style="vertical-align:top;",
629                    )
630                ),
631                class_='tracgooglemaps'
632            )
633
634        else:
635            directions = ""
636            mapnmore = mapdiv
637
638        # put everything in a tidy div
639        html = tag.div(
640            [
641                # Initialization script for this map
642                tag.script(Markup(_javascript_code % {
643                    'id': id,
644                    'center': center,
645                    'zoom': zoom, 'address': address,
646                    'type': type, 'width': width, 'height': height,
647                    'types_str': types_str, 'controls_str': controls_str,
648                    'markers_str': markers_str, 'directions': directions,
649                }), type='text/javascript'),
650                mapnmore
651            ],
652            class_='tracgooglemap-parent'
653        )
654
655        return html
Note: See TracBrowser for help on using the repository browser.