xref: /plugin/openlayersmap/syntax/olmap.php (revision 807d36ba696ee31bb38bbdcb77d6e88343dcf3c3)
1<?php
2/*
3 * Copyright (c) 2008-2014 Mark C. Prins <mprins@users.sf.net>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17if (! defined ( 'DOKU_INC' ))
18	define ( 'DOKU_INC', realpath ( dirname ( __FILE__ ) . '/../../' ) . '/' );
19if (! defined ( 'DOKU_PLUGIN' ))
20	define ( 'DOKU_PLUGIN', DOKU_INC . 'lib/plugins/' );
21require_once (DOKU_PLUGIN . 'syntax.php');
22
23/**
24 * DokuWiki Plugin openlayersmap (Syntax Component).
25 * Provides for display of an OpenLayers based map in a wiki page.
26 *
27 * @author Mark Prins
28 */
29class syntax_plugin_openlayersmap_olmap extends DokuWiki_Syntax_Plugin {
30
31	/**
32	 * defaults of the known attributes of the olmap tag.
33	 */
34	private $dflt = array (
35			'id' => 'olmap',
36			'width' => '550px',
37			'height' => '450px',
38			'lat' => 50.0,
39			'lon' => 5.1,
40			'zoom' => 12,
41			'statusbar' => true,
42			'controls' => true,
43			'poihoverstyle' => false,
44			'baselyr' => 'OpenStreetMap',
45			'gpxfile' => '',
46			'kmlfile' => '',
47			'geojsonfile' => '',
48			'summary' => ''
49	);
50
51	/**
52	 *
53	 * @see DokuWiki_Syntax_Plugin::getType()
54	 */
55	function getType() {
56		return 'substition';
57	}
58
59	/**
60	 *
61	 * @see DokuWiki_Syntax_Plugin::getPType()
62	 */
63	function getPType() {
64		return 'block';
65	}
66
67	/**
68	 *
69	 * @see Doku_Parser_Mode::getSort()
70	 */
71	function getSort() {
72		return 901;
73	}
74
75	/**
76	 *
77	 * @see Doku_Parser_Mode::connectTo()
78	 */
79	function connectTo($mode) {
80		$this->Lexer->addSpecialPattern ( '<olmap ?[^>\n]*>.*?</olmap>', $mode, 'plugin_openlayersmap_olmap' );
81	}
82
83	/**
84	 *
85	 * @see DokuWiki_Syntax_Plugin::handle()
86	 */
87	function handle($match, $state, $pos, &$handler) {
88		// break matched cdata into its components
89		list ( $str_params, $str_points ) = explode ( '>', substr ( $match, 7, - 9 ), 2 );
90		// get the lat/lon for adding them to the metadata (used by geotag)
91		preg_match ( '(lat[:|=]\"-?\d*\.\d*\")', $match, $mainLat );
92		preg_match ( '(lon[:|=]\"-?\d*\.\d*\")', $match, $mainLon );
93		$mainLat = substr ( $mainLat [0], 5, - 1 );
94		$mainLon = substr ( $mainLon [0], 5, - 1 );
95
96		$gmap = $this->_extract_params ( $str_params );
97		$overlay = $this->_extract_points ( $str_points );
98		$_firstimageID = '';
99
100		// choose maptype based on the specified tag
101		$imgUrl = "{{";
102		if (stripos ( $gmap ['baselyr'], 'google' ) !== false) {
103			// use google
104			$imgUrl .= $this->_getGoogle ( $gmap, $overlay );
105			$imgUrl .= "&.png";
106		} elseif (stripos ( $gmap ['baselyr'], 've' ) !== false) {
107			// use bing, note that VE is deprecated
108			$imgUrl .= $this->_getBing ( $gmap, $overlay );
109			$imgUrl .= "&.png";
110		} elseif (stripos ( $gmap ['baselyr'], 'bing' ) !== false) {
111			// use bing
112			$imgUrl .= $this->_getBing ( $gmap, $overlay );
113			$imgUrl .= "&.png";
114		} elseif (stripos ( $gmap ['baselyr'], 'mapquest' ) !== false) {
115			if ($this->getConf ( 'optionStaticMapGenerator' ) == 'remote') {
116				// use mapquest remote
117				$imgUrl .= $this->_getMapQuest ( $gmap, $overlay );
118				$imgUrl .= "&.png";
119			} else {
120				$_firstimageID = $this->_getStaticOSM ( $gmap, $overlay );
121				$imgUrl .= $_firstimageID;
122			}
123		} else {
124			// fall back to OpenStreetMap
125			$_firstimageID = $this->_getStaticOSM ( $gmap, $overlay );
126			$imgUrl .= $_firstimageID;
127			if ($this->getConf ( 'optionStaticMapGenerator' ) == 'remote') {
128				$imgUrl .= "&.png";
129			}
130		}
131
132		// append dw p_render specific params and render
133		$imgUrl .= "?" . str_replace ( "px", "", $gmap ['width'] ) . "x" . str_replace ( "px", "", $gmap ['height'] );
134		$imgUrl .= "&nolink";
135		$imgUrl .= " |" . $gmap ['summary'] . " }}";
136
137		$mapid = $gmap ['id'];
138		// create a javascript parameter string for the map
139		$param = '';
140		foreach ( $gmap as $key => $val ) {
141			$param .= is_numeric ( $val ) ? "$key: $val, " : "$key: '" . hsc ( $val ) . "', ";
142		}
143		if (! empty ( $param )) {
144			$param = substr ( $param, 0, - 2 );
145		}
146		unset ( $gmap ['id'] );
147
148		// create a javascript serialisation of the point data
149		$poi = '';
150		$poitable = '';
151		$rowId = 0;
152		if (! empty ( $overlay )) {
153			foreach ( $overlay as $data ) {
154				list ( $lat, $lon, $text, $angle, $opacity, $img ) = $data;
155				$rowId ++;
156				$poi .= ", {lat: $lat, lon: $lon, txt: '$text', angle: $angle, opacity: $opacity, img: '$img', rowId: $rowId}";
157				$poitable .= '
158						<tr>
159						<td class="rowId">' . $rowId . '</td>
160						<td class="icon"><img src="' . DOKU_BASE . 'lib/plugins/openlayersmap/icons/' . $img . '" alt="icon" /></td>
161						<td class="lat" title="' . $this->getLang ( 'olmapPOIlatTitle' ) . '">' . $lat . '</td>
162						<td class="lon" title="' . $this->getLang ( 'olmapPOIlonTitle' ) . '">' . $lon . '</td>
163						<td class="txt">' . $text . '</td>
164						</tr>';
165			}
166			$poi = substr ( $poi, 2 );
167		}
168		if (! empty ( $gmap ['kmlfile'] )) {
169			$poitable .= '
170					<tr>
171					<td class="rowId"><img src="' . DOKU_BASE . 'lib/plugins/openlayersmap/toolbar/kml_file.png" alt="KML icon" /></td>
172					<td class="icon"><img src="' . DOKU_BASE . 'lib/plugins/openlayersmap/toolbar/kml_line.png" alt="icon" /></td>
173					<td class="txt" colspan="3">KML track: ' . $this->getFileName ( $gmap ['kmlfile'] ) . '</td>
174					</tr>';
175		}
176		if (! empty ( $gmap ['gpxfile'] )) {
177			$poitable .= '
178					<tr>
179					<td class="rowId"><img src="' . DOKU_BASE . 'lib/plugins/openlayersmap/toolbar/gpx_file.png" alt="GPX icon" /></td>
180					<td class="icon"><img src="' . DOKU_BASE . 'lib/plugins/openlayersmap/toolbar/gpx_line.png" alt="icon" /></td>
181					<td class="txt" colspan="3">GPX track: ' . $this->getFileName ( $gmap ['gpxfile'] ) . '</td>
182					</tr>';
183		}
184		if (! empty ( $gmap ['geojsonfile'] )) {
185			$poitable .= '
186					<tr>
187					<td class="rowId"><img src="' . DOKU_BASE . 'lib/plugins/openlayersmap/toolbar/geojson_file.png" alt="KML icon" /></td>
188					<td class="icon"><img src="' . DOKU_BASE . 'lib/plugins/openlayersmap/toolbar/geojson_line.png" alt="icon" /></td>
189					<td class="txt" colspan="3">GeoJSON track: ' . $this->getFileName ( $gmap ['geojsonfile'] ) . '</td>
190					</tr>';
191		}
192
193		$js .= "{mapOpts:{" . $param . " },poi:[$poi]};";
194		// unescape the json
195		$poitable = stripslashes ( $poitable );
196
197		return array (
198				$mapid,
199				$js,
200				$mainLat,
201				$mainLon,
202				$poitable,
203				$gmap ['summary'],
204				$imgUrl,
205				$_firstimageID
206		);
207	}
208
209	/**
210	 *
211	 * @see DokuWiki_Syntax_Plugin::render()
212	 */
213	function render($mode, &$renderer, $data) {
214		// set to true after external scripts tags are written
215		static $initialised = false;
216		// incremented for each map tag in the page source so we can keep track of each map in this page
217		static $mapnumber = 0;
218
219		// dbglog($data, 'olmap::render() data.');
220		list ( $mapid, $param, $mainLat, $mainLon, $poitable, $poitabledesc, $staticImgUrl, $_firstimage ) = $data;
221
222		if ($mode == 'xhtml') {
223			$olscript = '';
224			$olEnable = false;
225			$gscript = '';
226			$gEnable = $this->getConf ( 'enableGoogle' );
227			$mqEnable = $this->getConf ( 'enableMapQuest' );
228			$osmEnable = $this->getConf ( 'enableOSM' );
229			$enableBing = $this->getConf ( 'enableBing' );
230
231			$scriptEnable = '';
232			if (! $initialised) {
233				$initialised = true;
234				// render necessary script tags
235				if ($gEnable) {
236					$gscript = '<script type="text/javascript" src="http://maps.google.com/maps/api/js?v=3.11&amp;sensor=false"></script>';
237				}
238				//$olscript = $this->getConf ( 'olScriptUrl' );
239				$olscript = '<script type="text/javascript" src="' . DOKU_BASE . 'lib/plugins/openlayersmap/lib/OpenLayers.js"></script>';
240				//$olscript = str_replace ( 'DOKU_BASE/', DOKU_BASE, $olscript );
241
242				$scriptEnable = '<script type="text/javascript" charset="utf-8">/*<![CDATA[*/';
243				$scriptEnable .= $olscript ? 'olEnable = true;' : 'olEnable = false;';
244				$scriptEnable .= 'gEnable = ' . ($gEnable ? 'true' : 'false') . ';';
245				$scriptEnable .= 'osmEnable = ' . ($osmEnable ? 'true' : 'false') . ';';
246				$scriptEnable .= 'mqEnable = ' . ($mqEnable ? 'true' : 'false') . ';';
247				$scriptEnable .= 'bEnable = ' . ($enableBing ? 'true' : 'false') . ';';
248				$scriptEnable .= 'bApiKey="' . $this->getConf ( 'bingAPIKey' ) . '";';
249				// $scriptEnable .= 'OpenLayers.ImgPath = "' . DOKU_BASE . 'lib/plugins/openlayersmap/lib/img' . $this->getConf ( 'olMapStyle' ) . '/";';
250				$scriptEnable .= '/*!]]>*/</script>';
251			}
252			$renderer->doc .= "
253			$gscript
254			$olscript
255			$scriptEnable";
256
257			if ($this->getConf ( 'enableA11y' )) {
258				$renderer->doc .= '<div id="' . $mapid . '-static" class="olStaticMap">' . p_render ( $mode, p_get_instructions ( $staticImgUrl ), $info ) . '</div>';
259			}
260			$renderer->doc .= '<div id="' . $mapid . '-clearer" class="clearer"><p>&nbsp;</p></div>';
261			if ($this->getConf ( 'enableA11y' )) {
262				// render a (initially hidden) table of the POI for the print and a11y presentation
263				$renderer->doc .= '<div class="olPOItableSpan" id="' . $mapid . '-table-span"><table class="olPOItable" id="' . $mapid . '-table" summary="' . $poitabledesc . '" title="' . $this->getLang ( 'olmapPOItitle' ) . '">
264<caption class="olPOITblCaption">' . $this->getLang ( 'olmapPOItitle' ) . '</caption>
265<thead class="olPOITblHeader">
266<tr>
267<th class="rowId" scope="col">id</th>
268<th class="icon" scope="col">' . $this->getLang ( 'olmapPOIicon' ) . '</th>
269<th class="lat" scope="col" title="' . $this->getLang ( 'olmapPOIlatTitle' ) . '">' . $this->getLang ( 'olmapPOIlat' ) . '</th>
270<th class="lon" scope="col" title="' . $this->getLang ( 'olmapPOIlonTitle' ) . '">' . $this->getLang ( 'olmapPOIlon' ) . '</th>
271<th class="txt" scope="col">' . $this->getLang ( 'olmapPOItxt' ) . '</th>
272</tr>
273</thead>';
274				if ($poitabledesc != '') {
275					$renderer->doc .= '<tfoot class="olPOITblFooter"><tr><td colspan="5">' . $poitabledesc . '</td></tr></tfoot>';
276				}
277				$renderer->doc .= '<tbody class="olPOITblBody">' . $poitable . '</tbody>
278						</table></div>';
279			}
280			// render inline mapscript parts
281			$renderer->doc .= '<script type="text/javascript" charset="utf-8">/*<![CDATA[*/';
282			$renderer->doc .= " olMapData[$mapnumber] = $param /*!]]>*/</script>";
283			$mapnumber ++;
284			return true;
285		} elseif ($mode == 'metadata') {
286			if (! (($this->dflt ['lat'] == $mainLat) && ($thisdflt ['lon'] == $mainLon))) {
287				// render geo metadata, unless they are the default
288				$renderer->meta ['geo'] ['lat'] = $mainLat;
289				$renderer->meta ['geo'] ['lon'] = $mainLon;
290				if ($geophp = &plugin_load ( 'helper', 'geophp' )) {
291					// if we have the geoPHP helper, add the geohash
292					// fails with older php versions.. $renderer->meta['geo']['geohash'] = (new Point($mainLon,$mainLat))->out('geohash');
293					$p = new Point ( $mainLon, $mainLat );
294					$renderer->meta ['geo'] ['geohash'] = $p->out ( 'geohash' );
295				}
296			}
297
298			if (($this->getConf ( 'enableA11y' )) && (! empty ( $_firstimage ))) {
299				// add map local image into relation/firstimage if not already filled and when it is a local image
300
301				global $ID;
302				$rel = p_get_metadata ( $ID, 'relation', METADATA_RENDER_USING_CACHE );
303				$img = $rel ['firstimage'];
304				if (empty ( $img ) /* || $img == $_firstimage*/){
305					dbglog ( $_firstimage, 'olmap::render#rendering image relation metadata for _firstimage as $img was empty or the same.' );
306					// This seems to never work; the firstimage entry in the .meta file is empty
307					// $renderer->meta['relation']['firstimage'] = $_firstimage;
308
309					// ... and neither does this; the firstimage entry in the .meta file is empty
310					// $relation = array('relation'=>array('firstimage'=>$_firstimage));
311					// p_set_metadata($ID, $relation, false, false);
312
313					// ... this works
314					$renderer->internalmedia ( $_firstimage, $poitabledesc );
315				}
316			}
317			return true;
318		}
319		return false;
320	}
321
322	/**
323	 * extract parameters for the map from the parameter string
324	 *
325	 * @param string $str_params
326	 *        	string of key="value" pairs
327	 * @return array associative array of parameters key=>value
328	 */
329	private function _extract_params($str_params) {
330		$param = array ();
331		preg_match_all ( '/(\w*)="(.*?)"/us', $str_params, $param, PREG_SET_ORDER );
332		// parse match for instructions, break into key value pairs
333		$gmap = $this->dflt;
334		foreach ( $param as $kvpair ) {
335			list ( $match, $key, $val ) = $kvpair;
336			$key = strtolower ( $key );
337			if (isset ( $gmap [$key] )) {
338				if ($key == 'summary') {
339					// preserve case for summary field
340					$gmap [$key] = $val;
341				} elseif ($key == 'id') {
342					// preserve case for id field
343					$gmap [$key] = $val;
344				} else {
345					$gmap [$key] = strtolower ( $val );
346				}
347			}
348		}
349		return $gmap;
350	}
351
352	/**
353	 * extract overlay points for the map from the wiki syntax data
354	 *
355	 * @param string $str_points
356	 *        	multi-line string of lat,lon,text triplets
357	 * @return array multi-dimensional array of lat,lon,text triplets
358	 */
359	private function _extract_points($str_points) {
360		$point = array ();
361		// preg_match_all('/^([+-]?[0-9].*?),\s*([+-]?[0-9].*?),(.*?),(.*?),(.*?),(.*)$/um', $str_points, $point, PREG_SET_ORDER);
362		/*
363		 * group 1: ([+-]?[0-9]+(?:\.[0-9]*)?) group 2: ([+-]?[0-9]+(?:\.[0-9]*)?) group 3: (.*?) group 4: (.*?) group 5: (.*?) group 6: (.*)
364		 */
365		preg_match_all ( '/^([+-]?[0-9]+(?:\.[0-9]*)?),\s*([+-]?[0-9]+(?:\.[0-9]*)?),(.*?),(.*?),(.*?),(.*)$/um', $str_points, $point, PREG_SET_ORDER );
366		// create poi array
367		$overlay = array ();
368		foreach ( $point as $pt ) {
369			list ( $match, $lat, $lon, $angle, $opacity, $img, $text ) = $pt;
370			$lat = is_numeric ( $lat ) ? $lat : 0;
371			$lon = is_numeric ( $lon ) ? $lon : 0;
372			$angle = is_numeric ( $angle ) ? $angle : 0;
373			$opacity = is_numeric ( $opacity ) ? $opacity : 0.8;
374			// TODO validate using exist & set up default img?
375			$img = trim ( $img );
376			$text = p_get_instructions ( $text );
377			// dbg ( $text );
378			$text = p_render ( "xhtml", $text, $info );
379			// dbg ( $text );
380			$text = addslashes ( str_replace ( "\n", "", $text ) );
381			$overlay [] = array (
382					$lat,
383					$lon,
384					$text,
385					$angle,
386					$opacity,
387					$img
388			);
389		}
390		return $overlay;
391	}
392
393	/**
394	 * Create a MapQuest static map API image url.
395	 *
396	 * @param array $gmap
397	 * @param array $overlay
398	 */
399	private function _getMapQuest($gmap, $overlay) {
400		$sUrl = $this->getConf ( 'iconUrlOverload' );
401		if (! $sUrl) {
402			$sUrl = DOKU_URL;
403		}
404		switch ($gmap ['baselyr']) {
405			case 'mapquest hybrid' :
406				$maptype = 'hyb';
407				break;
408			case 'mapquest sat' :
409				// because sat coverage is very limited use 'hyb' instead of 'sat' so we don't get a blank map
410				$maptype = 'hyb';
411				break;
412			case 'mapquest road' :
413			default :
414				$maptype = 'map';
415				break;
416		}
417		$imgUrl = "http://open.mapquestapi.com/staticmap/v4/getmap?declutter=true&";
418		if (count ( $overlay ) < 1) {
419			$imgUrl .= "?center=" . $gmap ['lat'] . "," . $gmap ['lon'];
420			// max level for mapquest is 16
421			if ($gmap ['zoom'] > 16) {
422				$imgUrl .= "&zoom=16";
423			} else {
424				$imgUrl .= "&zoom=" . $gmap ['zoom'];
425			}
426		}
427		// use bestfit instead of center/zoom, needs upperleft/lowerright corners
428		// $bbox=$this->_calcBBOX($overlay, $gmap['lat'], $gmap['lon']);
429		// $imgUrl .= "bestfit=".$bbox['minlat'].",".$bbox['maxlon'].",".$bbox['maxlat'].",".$bbox['minlon'];
430
431		// TODO declutter option works well for square maps but not for rectangular, maybe compensate for that or compensate the mbr..
432		$imgUrl .= "&size=" . str_replace ( "px", "", $gmap ['width'] ) . "," . str_replace ( "px", "", $gmap ['height'] );
433
434		// TODO mapquest allows using one image url with a multiplier $NUMBER eg:
435		// $NUMBER = 2
436		// $imgUrl .= DOKU_URL."/".DOKU_PLUGIN."/".getPluginName()."/icons/".$img.",$NUMBER,C,".$lat1.",".$lon1.",0,0,0,0,C,".$lat2.",".$lon2.",0,0,0,0";
437		if (! empty ( $overlay )) {
438			$imgUrl .= "&xis=";
439			foreach ( $overlay as $data ) {
440				list ( $lat, $lon, $text, $angle, $opacity, $img ) = $data;
441				// $imgUrl .= $sUrl."lib/plugins/openlayersmap/icons/".$img.",1,C,".$lat.",".$lon.",0,0,0,0,";
442				$imgUrl .= $sUrl . "lib/plugins/openlayersmap/icons/" . $img . ",1,C," . $lat . "," . $lon . ",";
443			}
444			$imgUrl = substr ( $imgUrl, 0, - 1 );
445		}
446		$imgUrl .= "&imageType=png&type=" . $maptype;
447		// dbglog($imgUrl,'syntax_plugin_openlayersmap_olmap::_getMapQuest: MapQuest image url is:');
448		return $imgUrl;
449	}
450
451	/**
452	 * Create a Google maps static image url w/ the poi.
453	 *
454	 * @param array $gmap
455	 * @param array $overlay
456	 */
457	private function _getGoogle($gmap, $overlay) {
458		$sUrl = $this->getConf ( 'iconUrlOverload' );
459		if (! $sUrl) {
460			$sUrl = DOKU_URL;
461		}
462		switch ($gmap ['baselyr']) {
463			case 'google hybrid' :
464				$maptype = 'hybrid';
465				break;
466			case 'google sat' :
467				$maptype = 'satellite';
468				break;
469			case 'google relief' :
470				$maptype = 'terrain';
471				break;
472			case 'google road' :
473			default :
474				$maptype = 'roadmap';
475				break;
476		}
477		// TODO maybe use viewport / visible instead of center/zoom,
478		// see: https://code.google.com/intl/nl/apis/maps/documentation/staticmaps/#ImplicitPositioning
479		// http://maps.google.com/maps/api/staticmap?center=51.565690,5.456756&zoom=16&size=600x400&markers=icon:http://wild-water.nl/dokuwiki/lib/plugins/openlayersmap/icons/marker.png|label:1|51.565690,5.456756&markers=icon:http://wild-water.nl/dokuwiki/lib/plugins/openlayersmap/icons/marker-blue.png|51.566197,5.458966|label:2&markers=icon:http://wild-water.nl/dokuwiki/lib/plugins/openlayersmap/icons/parking.png|51.567177,5.457909|label:3&markers=icon:http://wild-water.nl/dokuwiki/lib/plugins/openlayersmap/icons/parking.png|51.566283,5.457330|label:4&markers=icon:http://wild-water.nl/dokuwiki/lib/plugins/openlayersmap/icons/parking.png|51.565630,5.457695|label:5&sensor=false&format=png&maptype=roadmap
480		$imgUrl = "http://maps.google.com/maps/api/staticmap?sensor=false";
481		$imgUrl .= "&size=" . str_replace ( "px", "", $gmap ['width'] ) . "x" . str_replace ( "px", "", $gmap ['height'] );
482		$imgUrl .= "&center=" . $gmap ['lat'] . "," . $gmap ['lon'];
483		// max is 21 (== building scale), but that's overkill..
484		if ($gmap ['zoom'] > 17) {
485			$imgUrl .= "&zoom=17";
486		} else {
487			$imgUrl .= "&zoom=" . $gmap ['zoom'];
488		}
489
490		if (! empty ( $overlay )) {
491			$rowId = 0;
492			foreach ( $overlay as $data ) {
493				list ( $lat, $lon, $text, $angle, $opacity, $img ) = $data;
494				$imgUrl .= "&markers=icon%3a" . $sUrl . "lib/plugins/openlayersmap/icons/" . $img . "%7c" . $lat . "," . $lon . "%7clabel%3a" . ++ $rowId;
495			}
496		}
497		$imgUrl .= "&format=png&maptype=" . $maptype;
498		global $conf;
499		$imgUrl .= "&language=" . $conf ['lang'];
500		// dbglog($imgUrl,'syntax_plugin_openlayersmap_olmap::_getGoogle: Google image url is:');
501		return $imgUrl;
502	}
503
504	/**
505	 * Create a Bing maps static image url w/ the poi.
506	 *
507	 * @param array $gmap
508	 * @param array $overlay
509	 */
510	private function _getBing($gmap, $overlay) {
511		if (! $this->getConf ( 'bingAPIKey' )) {
512			// in case there is no Bing api key we'll use OSM
513			$this->_getStaticOSM ( $gmap, $overlay );
514		}
515		switch ($gmap ['baselyr']) {
516			case 've hybrid' :
517			case 'bing hybrid' :
518				$maptype = 'AerialWithLabels';
519				break;
520			case 've sat' :
521			case 'bing sat' :
522				$maptype = 'Aerial';
523				break;
524			case 've normal' :
525			case 've road' :
526			case 've' :
527			case 'bing road' :
528			default :
529				$maptype = 'Road';
530				break;
531		}
532		$imgUrl = "http://dev.virtualearth.net/REST/v1/Imagery/Map/" . $maptype . "/";
533		if (! $this->getConf ( 'autoZoomMap' )) {
534			$bbox = $this->_calcBBOX ( $overlay, $gmap ['lat'], $gmap ['lon'] );
535			$imgUrl .= "?mapArea=" . $bbox ['minlat'] . "," . $bbox ['minlon'] . "," . $bbox ['maxlat'] . "," . $bbox ['maxlon'];
536			$imgUrl .= "&declutter=1";
537			// or
538			// $imgUrl .= $gmap['lat'].",".$gmap['lon']."/".$gmap['zoom']."?";
539		}
540		if (strpos ( $imgUrl, "?" ) === false)
541			$imgUrl .= "?";
542
543		$imgUrl .= "&ms=" . str_replace ( "px", "", $gmap ['width'] ) . "," . str_replace ( "px", "", $gmap ['height'] );
544		$imgUrl .= "&key=" . $this->getConf ( 'bingAPIKey' );
545		if (! empty ( $overlay )) {
546			$rowId = 0;
547			foreach ( $overlay as $data ) {
548				list ( $lat, $lon, $text, $angle, $opacity, $img ) = $data;
549				// TODO icon style lookup, see: http://msdn.microsoft.com/en-us/library/ff701719.aspx for iconStyle
550				$iconStyle = 32;
551				$rowId ++;
552				// NOTE: the max number of pushpins is 18! or we have to use POST (http://msdn.microsoft.com/en-us/library/ff701724.aspx)
553				if ($rowId == 18) {
554					break;
555				}
556				$imgUrl .= "&pp=$lat,$lon;$iconStyle;$rowId";
557			}
558		}
559		// dbglog($imgUrl,'syntax_plugin_openlayersmap_olmap::_getBing: bing image url is:');
560		return $imgUrl;
561	}
562
563	/**
564	 * Create a static OSM map image url w/ the poi from http://staticmap.openstreetmap.de (staticMapLite)
565	 * use http://staticmap.openstreetmap.de "staticMapLite" or a local version
566	 *
567	 * @param array $gmap
568	 * @param array $overlay
569	 *
570	 * @todo implementation for http://ojw.dev.openstreetmap.org/StaticMapDev/
571	 */
572	private function _getStaticOSM($gmap, $overlay) {
573		// http://staticmap.openstreetmap.de/staticmap.php?center=47.000622235634,10.117187497601&zoom=5&size=500x350
574		// &markers=48.999812532766,8.3593749976708,lightblue1|43.154850037315,17.499999997306,lightblue1|49.487527053077,10.820312497573,ltblu-pushpin|47.951071133739,15.917968747369,ol-marker|47.921629720114,18.027343747285,ol-marker-gold|47.951071133739,19.257812497236,ol-marker-blue|47.180141361692,19.257812497236,ol-marker-green
575		global $conf;
576
577		if ($this->getConf ( 'optionStaticMapGenerator' ) == 'local') {
578			if (! $my = &plugin_load ( 'helper', 'openlayersmap_staticmap' )) {
579				dbglog ( $my, 'syntax_plugin_openlayersmap_olmap::_getStaticOSM: openlayersmap_staticmap plugin is not available.' );
580			}
581			if (! $geophp = &plugin_load ( 'helper', 'geophp' )) {
582				dbglog ( $geophp, 'syntax_plugin_openlayersmap_olmap::_getStaticOSM: geophp plugin is not available.' );
583			}
584			$size = str_replace ( "px", "", $gmap ['width'] ) . "x" . str_replace ( "px", "", $gmap ['height'] );
585
586			$markers = '';
587			if (! empty ( $overlay )) {
588				foreach ( $overlay as $data ) {
589					list ( $lat, $lon, $text, $angle, $opacity, $img ) = $data;
590					$iconStyle = substr ( $img, 0, strlen ( $img ) - 4 );
591					$markers [] = array (
592							'lat' => $lat,
593							'lon' => $lon,
594							'type' => $iconStyle
595					);
596				}
597			}
598
599			$result = $my->getMap ( $gmap ['lat'], $gmap ['lon'], $gmap ['zoom'], $size, $maptype, $markers, $gmap ['gpxfile'], $gmap ['kmlfile'], $gmap ['geojsonfile'] );
600		} else {
601			// default to external provider
602			$imgUrl = "http://staticmap.openstreetmap.de/staticmap.php";
603			$imgUrl .= "?center=" . $gmap ['lat'] . "," . $gmap ['lon'];
604			$imgUrl .= "&size=" . str_replace ( "px", "", $gmap ['width'] ) . "x" . str_replace ( "px", "", $gmap ['height'] );
605
606			if ($gmap ['zoom'] > 16) {
607				// actually this could even be 18, but that seems overkill
608				$imgUrl .= "&zoom=16";
609			} else {
610				$imgUrl .= "&zoom=" . $gmap ['zoom'];
611			}
612
613			switch ($gmap ['baselyr']) {
614				case 'mapnik' :
615				case 'openstreetmap' :
616					$maptype = 'openstreetmap';
617					break;
618				case 'transport' :
619					$maptype = 'transport';
620					break;
621				case 'landscape' :
622					$maptype = 'landscape';
623					break;
624				case 'cycle map' :
625					$maptype = 'cycle';
626					break;
627				case 'cloudmade' :
628					$maptype = 'cloudmade';
629					break;
630				case 'cloudmade fresh' :
631					$maptype = 'fresh';
632					break;
633				case 'hike and bike map' :
634					$maptype = 'hikeandbike';
635					break;
636				case 'mapquest hybrid' :
637				case 'mapquest road' :
638				case 'mapquest sat' :
639					$maptype = 'mapquest';
640					break;
641				default :
642					$maptype = '';
643					break;
644			}
645			$imgUrl .= "&maptype=" . $maptype;
646
647			if (! empty ( $overlay )) {
648				$rowId = 0;
649				$imgUrl .= "&markers=";
650				foreach ( $overlay as $data ) {
651					list ( $lat, $lon, $text, $angle, $opacity, $img ) = $data;
652					$rowId ++;
653					$iconStyle = "lightblue$rowId";
654					$imgUrl .= "$lat,$lon,$iconStyle%7c";
655				}
656				$imgUrl = substr ( $imgUrl, 0, - 3 );
657			}
658
659			$result = $imgUrl;
660		}
661
662		// dbglog($result,'syntax_plugin_openlayersmap_olmap::_getStaticOSM: osm image url is:');
663		return $result;
664	}
665
666	/**
667	 * Calculate the minimum bbox for a start location + poi.
668	 *
669	 * @param array $overlay
670	 *        	multi-dimensional array of array($lat, $lon, $text, $angle, $opacity, $img)
671	 * @param float $lat
672	 *        	latitude for map center
673	 * @param float $lon
674	 *        	longitude for map center
675	 * @return multitype:float array describing the mbr and center point
676	 */
677	private function _calcBBOX($overlay, $lat, $lon) {
678		$lats [] = $lat;
679		$lons [] = $lon;
680		foreach ( $overlay as $data ) {
681			list ( $lat, $lon, $text, $angle, $opacity, $img ) = $data;
682			$lats [] = $lat;
683			$lons [] = $lon;
684		}
685		sort ( $lats );
686		sort ( $lons );
687		// TODO: make edge/wrap around cases work
688		$centerlat = $lats [0] + ($lats [count ( $lats ) - 1] - $lats [0]);
689		$centerlon = $lons [0] + ($lons [count ( $lats ) - 1] - $lons [0]);
690		return array (
691				'minlat' => $lats [0],
692				'minlon' => $lons [0],
693				'maxlat' => $lats [count ( $lats ) - 1],
694				'maxlon' => $lons [count ( $lats ) - 1],
695				'centerlat' => $centerlat,
696				'centerlon' => $centerlon
697		);
698	}
699
700	/**
701	 * Figures out the base filename of a media path.
702	 *
703	 * @param String $mediaLink
704	 */
705	private function getFileName($mediaLink) {
706		$mediaLink = str_replace ( '[[', '', $mediaLink );
707		$mediaLink = str_replace ( ']]', '', $mediaLink );
708		$mediaLink = substr ( $mediaLink, 0, - 4 );
709		$parts = explode ( ':', $mediaLink );
710		$mediaLink = end ( $parts );
711		return str_replace ( '_', ' ', $mediaLink );
712	}
713}