====== 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 ==== */ 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"; // 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
$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
, otherwise maps start overlappig. $renderer->doc .= '>
'; 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 .= ""; if ($state == DOKU_LEXER_SPECIAL) { $renderer->doc .= "$text"; } return true; case DOKU_LEXER_UNMATCHED: $renderer->doc .= $renderer->_xmlEntities($match); return true; case DOKU_LEXER_EXIT: $renderer->doc .= ''; return true; default: //$renderer->doc .= "
Cannot handle mode $style
"; } } return false; } } ?>
==== script.js ==== /** * 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 */ (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, '
' + place.address + '
' + 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); })();
==== style.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%; } 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