DokuWiki

It's better when it's simple

User Tools

Site Tools


plugin:google_maps

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
plugin:google_maps [2009-02-07 15:02] – Added more tags 201.83.249.204plugin:google_maps [2019-04-22 09:45] (current) – [Bugs and ToDo] hua
Line 1: Line 1:
 +====== Google Maps plugin ======
 +
 +---- plugin ----
 +description: Embeds a Google Map into a wiki page
 +author     : Dmitry Katsubo
 +email      : dma_k@mail.ru
 +type       : syntax
 +lastupdate : 2010-10-18
 +compatible : 2009-12-25c, 2008-05-05, 2006-11-06, 2016-06-26a
 +depends    : 
 +conflicts 
 +similar    : googlemaps
 +tags       : media, maps, google, embed
 +
 +downloadurl: https://www.centurion.link/w/_media/plugin/google-2016-09-20.tar.bz2
 +----
 +
 +
 +===== Download and Installation =====
 +
 +Search and install the plugin using the [[plugin:extension|Extension Manager]]. Refer to [[:Plugins]] on how to install plugins manually.
 +
 +[[https://www.centurion.link/w/plugin/google_maps|Up-to-date version of the plugin and this description]].
 +
 +
 +===== Description & Requirements =====
 +
 +Plugin allows to embed Google map frames to the page or create external links to Google Maps service. Useful when listing addresses.
 +Works with any browser, where [[http://maps.google.com|Google Maps]] works.
 +
 +
 +===== Syntax =====
 +
 +Complete list of all possible options:
 +
 +''%%{{googlemaps>address1;address2;address3[zoom=16,width=800,height=600,size=small,control=hierarchical,overviewmap=true,type=embedded]|alternative text}}%%''
 +
 +Google maps plugin can generate google maps in embedded frame (when **type=embedded**) or as link to google maps (when **type** is not set //(default)//).
 +
 +Alternative text may include any other inline formatting supported by dokuwiki, and is rendered only for **default type** (if one specify alternative text then this type is forced, as there is no opportunity to render it in **embedded** mode). If **alternative text** is skipped then **address** is used as a link text.
 +
 +**embedded type** supports several addresses to be marked on Google Maps (only first address will be used in **default type**). It is advised not to specify **zoom** in this case, which will be set automatically to optimal value to display all address markers on the map.
 +
 +
 +==== Parameters ====
 +
 +^  Parameter name ^  Possible valid values ^
 +| **zoom** | number from 1 to 19 which specifies the initial zoom; if not defined, then it is set to optimal value |
 +| **width** | size in px -- the width of embedded frame (overrides the default parameter value) |
 +| **height** | size in px -- the height of embedded frame (overrides the default parameter value) |
 +| **size** | **small** //(default)// -- generate small size window with limited set of controls \\ **large** -- generate large size window with all controls \\ The sizes for the frames are taken from **%%small_*%%** and **%%large_*%%** parameters below. |
 +| **control** | **hierarchical** //(default)// -- show two buttons in top-right corner: "Map" and "Satellite" \\ **all** -- show three buttons: "Map", "Satellite" and "Hybrid" \\ **none** -- show no buttons |
 +| **overviewmap** | **true** //(default)// -- show the link to overview map in right-bottom corner \\ **false** -- do not show overview map |
 +| **type** | **unset** //(default)// -- render the link to google maps \\ **embedded** -- render embedded frame with google maps |
 +
 +
 +==== Default options ====
 +
 +^  Parameter name ^  Possible valid values ^
 +| **google_api_key** | Valid Google API key for the current site. See [[#configuration]] for more details about how to generate it. |
 +| **small_width** | //(default is **425**)// size in px -- the width of small frame, if **size=small** was specified |
 +| **small_height** | //(default is **350**)// size in px -- the height of small frame, if **size=small** was specified |
 +| **large_width** | //(default is **550**)// size in px -- the width of large frame, if **size=large** was specified |
 +| **large_height** | //(default is **450**)// size in px -- the height of large frame, if **size=large** was specified |
 +
 +
 +===== Demonstration =====
 +
 +[[https://www.centurion.link/w/wiki/playground|Try the plugin]].
 +
 +
 +===== Configuration =====
 +
 +Important is to register your site at Google to receive an access to Google services. Registration is free:
 +  * [[http://code.google.com/apis/maps/signup.html|Register your website]] and receive you unique Google API key.
 +  * Modify the configuration file and put a newly generated key to **google_api_key**.
 +
 +
 +===== Documentation =====
 +
 +  * Plugin uses the JSON (JavaScript Object Notation) technique (described in [[http://www.xml.com/pub/a/2005/12/21/json-dynamic-script-tag.html?|JSON and the Dynamic Script Tag: Easy, XML-less Web Services for JavaScript]]) to access [[http://www.geonames.org/export/ws-overview.html|GeoNames WebServices]]. Examples:
 +    * [[http://www.geonames.org/maps/json-googlemaps-example.html|Example of GoogleMaps and GeoNames interaction]] with use of JSON technique
 +    * [[http://www.ewebsite.biz/modules.php?name=gMap2|Example of GoogleMaps]] when JS code is server-side generated
 +  * [[http://developer.yahoo.com/maps/rest/V1/geocode.html|Yahoo Maps WS]]. Example:
 +    * [[http://theurer.cc/code/devx/mapProxy.html|Example of and Yahoo Maps interaction]], which uses full-blown AJAX ''XMLHttpRequest'' technique
 +  * [[http://code.google.com/apis/maps/documentation/services.html|Google Geocoder]]. Example:
 +    * [[http://code.google.com/apis/maps/documentation/examples/geocoding-extraction.html|extended data access]]
 +  * See also [[http://code.google.com/apis/maps/documentation/reference.html#GMap2|GMap2]] and [[http://code.google.com/apis/maps/documentation/reference.html#GClientGeocoder|GClientGeocoder]] API reference
 +
 +
 +===== Source =====
 +
 +==== syntax/maps.php ====
 +
 +<code php>
 +<?php
 +/**
 + * Plugin google_maps: Generates embedded Google Maps frame or link to Google Maps.
 + 
 + * @license    GPLv2 (http://www.gnu.org/licenses/gpl.html)
 + * @author     Dmitry Katsubo <dma_k@mail.ru>
 + */
 +
 +if(!defined('DOKU_INC')) die();
 +if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
 +require_once(DOKU_PLUGIN.'syntax.php');
 +
 +/**
 + * All DokuWiki plugins to extend the parser/rendering mechanism
 + * need to inherit from this class
 + */
 +class syntax_plugin_google_maps extends DokuWiki_Syntax_Plugin
 +{
 +  private $RE_NON_SYNTAX_SEQ = '[^\[\]{}|]+';
 +  private $RE_PLUGIN_BODY;
 +
 +  function syntax_plugin_google_maps()
 +  {
 +    $this->RE_PLUGIN_BODY = $this->RE_NON_SYNTAX_SEQ . '(?:\\[' . $this->RE_NON_SYNTAX_SEQ . '\\])?';
 +  }
 +
 +  function getInfo()
 +  {
 +    return array(
 +      'author'  => 'Dmitry Katsubo',
 +      'email'    => 'dma_k@mail.ru',
 +      'date'    => '2016-09-20',
 +      'name'    => 'Google Maps Plugin',
 +      'desc'    => 'Adds a Google Maps frame 
 +         syntax: {{googlemaps>address1;address2;address3[zoom=16,size=small,control=hierarchical,overviewmap=true,width=800,height=600,type=embedded]|alternative text}}',
 +      'url'    => 'http://centurion.dynalias.com/wiki/plugin/google_maps',
 +    );
 +  }
 +
 +  function getAllowedTypes()
 +  {
 +    return array('formatting');
 +  }
 +
 +  function getType()
 +  {
 +    return 'substition';
 +  }
 +
 +  function getSort()
 +  {
 +    return 159;
 +  }
 +
 +  function connectTo($mode)
 +  {
 +    $this->Lexer->addSpecialPattern('{{googlemaps>' . $this->RE_PLUGIN_BODY . '}}', $mode, 'plugin_google_maps'); 
 +    $this->Lexer->addEntryPattern('{{googlemaps>' . $this->RE_PLUGIN_BODY . '\|(?=' . $this->RE_NON_SYNTAX_SEQ . '}})', $mode, 'plugin_google_maps');
 +  }
 +
 +  function postConnect()
 +  {
 +    $this->Lexer->addExitPattern('}}', 'plugin_google_maps');
 +  }
 +
 +  private function getConfigValue($options, $option_name, $config_prefix = null)
 +  {
 +    // Also escape HTML to protect the page:
 +    return(htmlspecialchars(
 +      isset($options[$option_name]) ?
 +        $options[$option_name] :
 +        $this->getConf($config_prefix . $option_name)
 +    ));
 +  }
 +
 +  function handle($match, $state, $pos, Doku_Handler $handler)
 +  {
 +    switch ($state)
 +    {
 +      case DOKU_LEXER_SPECIAL:
 +      case DOKU_LEXER_ENTER:
 +        $matches = array();
 +
 +        if (!preg_match('/{{googlemaps>(' . $this->RE_NON_SYNTAX_SEQ . ')(?:\\[(' . $this->RE_NON_SYNTAX_SEQ . ')\\])?/', $match, $matches))
 +        {
 +          return array('');  // this is an error
 +        }
 +
 +        $options = array();
 +
 +        if (isset($matches[2]))
 +        {
 +          $entries = explode(',', $matches[2]);
 +
 +          foreach ($entries as $entry)
 +          {
 +            $key_value = explode('=', $entry);
 +
 +            $options[trim($key_value[0])] = trim($key_value[1]);
 +          }
 +        }
 +
 +        return array($state, array($matches[1], &$options));
 +    }
 +    
 +    return array($state, $match);
 +  }
 +
 +  function render($mode, Doku_Renderer $renderer, $data)
 +  {
 +    if ($mode == 'xhtml')
 +    {
 +      list($state, $match) = $data;
 +
 +      switch($state)
 +      {
 +        case DOKU_LEXER_SPECIAL:
 +        case DOKU_LEXER_ENTER:
 +          list($text, $options) = $match;
 +
 +          // All locations are in this array:
 +          $locations = array();
 +          $i = 0;
 +
 +          foreach (explode(";", $text) as $q)
 +          {
 +            $q = trim($q);
 +            if (strlen($q))
 +            {
 +              $locations[$i++] = htmlspecialchars(html_entity_decode($q));
 +            }
 +          }
 +
 +          // This type is available only in DOKU_LEXER_SPECIAL state:
 +          if ($state == DOKU_LEXER_SPECIAL && $options['type'] == 'embedded')
 +          {
 +            // Dynamic injection of this script via JS causes FF to hang, so we have to include it for each map:
 +            $renderer->doc .= "\n<script type='text/javascript' src='//maps.google.com/maps?file=api&v=2.x&key=" . $this->getConf('google_api_key') . "'></script>";
 +
 +            // Default values:
 +            $size      = $this->getConfigValue($options, 'size');
 +            $width      = $this->getConfigValue($options, 'width', $size . '_') . "px";
 +            $height     = $this->getConfigValue($options, 'height', $size . '_') . "px";
 +
 +            // Embedded div:
 +            $renderer->doc .= "\n<div class='gmaps_frame' style='width: $width; height: $height'";
 +
 +            foreach ($locations as $i => $q)
 +            {
 +              $renderer->doc .= " location$i='$q'";
 +            }
 +
 +            // Copy values into attributes:
 +            foreach (array('size', 'control', 'overviewmap', 'zoom') as $attr_name)
 +            {
 +              $attr_value = $this->getConfigValue($options, $attr_name);
 +
 +              if (strlen($attr_value))
 +              {
 +                $renderer->doc .= ' ' . $attr_name . '="' . $attr_value . '"';
 +              }
 +            }
 +
 +            // Important to leave one hanging node inside <div>, otherwise maps start overlappig.
 +            $renderer->doc .= '></div>';
 +
 +            return true;
 +          }
 +
 +          // If we are here it means:
 +          // * state == DOKU_LEXER_SPECIAL and type != embedded ==> we render a link with a text equal to address, as there is no alternative text in this state
 +          // * state == DOKU_LEXER_ENTER   and type != embedded ==> we start rendering a link; the alternative text will be rendered by dokuwiki renderer and may include any formatting
 +          // * state == DOKU_LEXER_ENTER   and type == embedded ==> the is unsupported combination, but we render a link the same as with type != embedded
 +
 +          // Concat params:
 +          $params = '&';
 +          // If not defined, Google Maps engine will automatically select the best zoom:
 +          if ($options['zoom'])
 +          {
 +            $params .= "z=" . $options['zoom'];
 +          }
 +
 +          // Query is already escaped, params are taken from options:
 +          $url = "//maps.google.com/maps?q=$locations[0]$params";
 +
 +          // External link:
 +          $renderer->doc .= "<a href='$url' class='gmaps_link'>";
 +
 +          if ($state == DOKU_LEXER_SPECIAL)
 +          {
 +             $renderer->doc .= "$text</a>";
 +          }
 +
 +          return true;
 +
 +        case DOKU_LEXER_UNMATCHED:
 +          $renderer->doc .= $renderer->_xmlEntities($match);
 +          return true;
 +          
 +        case DOKU_LEXER_EXIT:
 +          $renderer->doc .= '</a>';
 +          return true;
 +        
 +        default:
 +          //$renderer->doc .= "<div class='error'>Cannot handle mode $style</div>";
 +      }
 +    }
 +
 +    return false;
 +  }
 +}
 +?>
 +</code>
 +
 +
 +==== script.js ====
 +
 +<code javascript>
 +/**
 + * Plugin google_maps: Generates embedded Google Maps frame or link to Google Maps.
 + *
 + * @license    GPLv2 (http://www.gnu.org/licenses/gpl.html)
 + * @author     Dmitry Katsubo <dma_k@mail.ru>
 + */
 +
 +(function() {
 +// Globals:
 +var GMAPS_MAX_RETRY_COUNT = 5;
 +var GMAPS_RETRY_DELAY = 100;
 +var GMAPS_MAX_GEO_RESULTS = 1;
 +var GMAPS_GEOCODER = null;
 +
 +/*
 + * This function creates a new marker with a given HTML shown when a marker is clicked.
 + */
 +function createMarker(point, desc)
 +{
 +  var marker = new GMarker(point);
 +
 +  // Note: Without wrapping into a function, listeners are added to the same objects!
 +  GEvent.addListener(marker, "click", function()
 +  {
 +    marker.openInfoWindowHtml(desc);
 +  });
 +
 +  return marker;
 +}
 +
 +/*
 + * This recursive function sends an ansynchronous query to Google GeoCoder and marks results on the map.
 + */
 +function queryGoogleGeo(map, bounds, locations, index, zoom, retry)
 +{
 +  if (GMAPS_GEOCODER == null)
 +  {
 +    // Can be initialized only at this point, as Google libraries should have been included:
 +    GMAPS_GEOCODER = new GClientGeocoder();
 +  }
 +
 +  GMAPS_GEOCODER.getLocations(locations[index],
 +    function generateMarkersFromGoogleGeoResult(response)
 +    {
 +      // Was not able to locate any data:
 +      if (response == null)
 +      {
 +        alert("No response from GeoCoder for location " + locations[index] + ". Giving up.");
 +        return;
 +      }
 +      else if (response.Status.code == 602)
 +      {
 +        if (retry++ >= GMAPS_MAX_RETRY_COUNT)
 +        {
 +          alert("The maximum amount of retries (" + GMAPS_MAX_RETRY_COUNT + ") has been reached for location " + locations[index] + ". Giving up.");
 +          return;
 +        }
 +
 +        setTimeout(queryGoogleGeo, GMAPS_RETRY_DELAY, map, bounds, locations, index, zoom, retry);
 +        return;
 +      }
 +      else if (response.Status.code != 200)
 +      {
 +        alert("Invalid response code (" + response.Status.code + ") from GeoCoder for location " + locations[index] + ". Giving up.");
 +        return;
 +      }
 +
 +      var places = response.Placemark;
 +
 +      for (var i = 0; i < places.length && i < GMAPS_MAX_GEO_RESULTS; i++)
 +      {
 +        var place = places[i];
 +        var point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);
 +
 +        bounds.extend(point);
 +
 +        map.addOverlay(createMarker(point, '<div class="gmaps_marker"><strong>' + place.address + '</strong><br/>'
 +          + place.AddressDetails.Country.CountryNameCode
 +        ));
 +      }
 +
 +      if (index == locations.length - 1)
 +      {
 +        if (zoom == null)
 +        {
 +          if (!bounds.isEmpty())
 +          {
 +            // We select the best zoom for the boundary:
 +            zoom = map.getBoundsZoomLevel(bounds);
 +          }
 +        }
 +        else
 +        {
 +          // zoom is required to be an integer:
 +          zoom = parseInt(zoom);
 +        }
 +
 +        map.setCenter(bounds.getCenter(), zoom);
 +      }
 +      else
 +      {
 +        // Query recuresively other locations:
 +        queryGoogleGeo(map, bounds, locations, index + 1, zoom, retry);
 +      }
 +    });
 +}
 +
 +/**
 + * Initialisation function. Creates Gmap objects and loads Geo information.
 + */
 +function loadMaps()
 +{
 +  jQuery('div.gmaps_frame').each(function() {
 +    var attrs = this.attributes;
 +
 +    // Create a map:
 +    var map = new GMap2(this);
 +    map.setCenter(new GLatLng(34, 0), 1); // default point
 +
 +    // left-top navigator and zoomer
 +    if (attrs.size.value == 'small')
 +      map.addControl(new GSmallMapControl());
 +    else if (attrs.size.value == 'large')
 +      map.addControl(new GLargeMapControl());
 +
 +    // right-top map type switch buttons
 +    if (attrs.control.value == 'hierarchical')
 +      map.addControl(new GHierarchicalMapTypeControl());
 +    else if (attrs.control.value == 'all')
 +      map.addControl(new GMapTypeControl());
 +
 +    // mini-map in the bottom-right corner
 +    if (attrs.overviewmap.value == 'true')
 +    {
 +      var overviewMap = new GOverviewMapControl();
 +      map.addControl(overviewMap);
 +      overviewMap.hide();
 +    }
 +
 +    map.enableScrollWheelZoom();
 +
 +    var locations = new Array();
 +
 +    var n = 0;
 +    while (true)
 +    {
 +      if (attrs['location' + n] == null)
 +      {
 +        break;
 +      }
 +
 +      locations[n] = attrs['location' + n].value;
 +      n++;
 +    }
 +
 +    queryGoogleGeo(map, new GLatLngBounds(), locations, 0, attrs.zoom == null ? null : attrs.zoom.value, 0);
 +  });
 +}
 +
 +// A special Wiki-wide function, defined in lib/scripts/events.js:
 +jQuery(loadMaps);
 +})();</code>
 +
 +
 +==== style.css ====
 +
 +<code css>
 +a.gmaps_link {
 +  background: transparent url(gmaps_link.png) no-repeat left center; /* "top" does not look nicely in headers; "center" creates artifacts when link is split into several lines */
 +  padding: 1px 0px 1px 12px;
 +}
 +
 +div.gmaps_frame {
 +  border: 1px solid __border__;
 +}
 +
 +div.gmaps_marker {
 +  font-size: 90%;
 +}
 +</code>
 +
 +The latest source code can be taken from [[https://github.com/dmak/dokuwiki/tree/master/lib/plugins/google|Github]].
 +
 +
 +===== Release History =====
 +
 +  * 2008-03-17 (r05) --- Initial version.
 +  * [[https://www.centurion.link/w/_media/plugin/google_maps-2008-04-12.tar.bz2|2008-04-12]]
 +  * [[https://www.centurion.link/w/_media/plugin/google_maps-2008-10-03.tar.bz2|2008-10-03 (r47)]] --- Plugin was refactored to allow formatting in comment. Also aligned in compliance with common practices how to process the flow.
 +  * [[https://www.centurion.link/w/_media/plugin/google-2010-10-15.tar.bz2|2010-10-15 (r205)]] --- Three google-related plugins are merged into one bundle. Multiple addresses support was added.
 +  * [[https://www.centurion.link/w/_media/plugin/google-2016-09-20.tar.bz2|2016-09-20]] --- HTTPS fix. API adapted for "Elenor of Tsort" DokuWiki release.
 +
 +
 +===== Bugs and ToDo =====
 +
 +Tested with IE 6.0 SP1, FF 3.5.x. No bugs at the moment.
 +
 +There is nothing right now in my ToDo list. Please, add comment to this page.
 +
 +Question:\\
 +1. Google API key should be placed in \doku\lib\plugins\google_maps\conf\default.php ?\\
 +It doesn't work for me somehow
  

Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Share Alike 4.0 International
CC Attribution-Share Alike 4.0 International Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki