xref: /plugin/openlayersmap/syntax/olmap.php (revision 4c9f9dfc2e4a278ecdacd71cd255525707c66e31)
1<?php
2/*
3 * Copyright (c) 2008-2016 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, Doku_Handler $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		if (!is_numeric($mainLat)) {
96			$mainLat = $this->dflt ['lat'];
97		}
98		if (!is_numeric($mainLon)) {
99			$mainLon = $this->dflt ['lon'];
100		}
101
102		$gmap = $this->_extract_params ( $str_params );
103		$overlay = $this->_extract_points ( $str_points );
104		$_firstimageID = '';
105
106		$_nocache = false;
107		// choose maptype based on the specified tag
108		$imgUrl = "{{";
109		if (stripos ( $gmap ['baselyr'], 'google' ) !== false) {
110			// Google
111			$imgUrl .= $this->_getGoogle ( $gmap, $overlay );
112			$imgUrl .= "&.png";
113		} elseif (stripos ( $gmap ['baselyr'], 'bing' ) !== false) {
114			// Bing
115			if (! $this->getConf ( 'bingAPIKey' )) {
116				// in case there is no Bing api key we'll use OSM
117				$_firstimageID = $this->_getStaticOSM ( $gmap, $overlay );
118				$imgUrl .= $_firstimageID;
119				if ($this->getConf ( 'optionStaticMapGenerator' ) == 'remote') {
120					$imgUrl .= "&.png";
121				}
122			} else {
123				// seems that Bing doesn't like the DW client, turn off caching
124				$_nocache = true;
125				$imgUrl .= $this->_getBing ( $gmap, $overlay ) . "&.png";
126			}
127		} /* elseif (stripos ( $gmap ['baselyr'], 'mapquest' ) !== false) {
128			// MapQuest
129			if (! $this->getConf ( 'mapquestAPIKey' )) {
130				// no API key for MapQuest, use OSM
131				$_firstimageID = $this->_getStaticOSM ( $gmap, $overlay );
132				$imgUrl .= $_firstimageID;
133				if ($this->getConf ( 'optionStaticMapGenerator' ) == 'remote') {
134					$imgUrl .= "&.png";
135				}
136			} else {
137				$imgUrl .= $this->_getMapQuest ( $gmap, $overlay );
138				$imgUrl .= "&.png";
139			}
140		} */ else {
141			// default OSM
142			$_firstimageID = $this->_getStaticOSM ( $gmap, $overlay );
143			$imgUrl .= $_firstimageID;
144			if ($this->getConf ( 'optionStaticMapGenerator' ) == 'remote') {
145				$imgUrl .= "&.png";
146			}
147		}
148
149		// append dw p_render specific params and render
150		$imgUrl .= "?" . str_replace ( "px", "", $gmap ['width'] ) . "x" . str_replace ( "px", "", $gmap ['height'] );
151		$imgUrl .= "&nolink";
152
153		// add nocache option for selected services
154		if ($_nocache) {
155			$imgUrl .= "&nocache";
156		}
157
158		$imgUrl .= " |".$gmap ['summary'] . " }}";
159
160		// dbglog($imgUrl,"complete image tags is:");
161
162		$mapid = $gmap ['id'];
163		// create a javascript parameter string for the map
164		$param = '';
165		foreach ( $gmap as $key => $val ) {
166			$param .= is_numeric ( $val ) ? "$key: $val, " : "$key: '" . hsc ( $val ) . "', ";
167		}
168		if (! empty ( $param )) {
169			$param = substr ( $param, 0, - 2 );
170		}
171		unset ( $gmap ['id'] );
172
173		// create a javascript serialisation of the point data
174		$poi = '';
175		$poitable = '';
176		$rowId = 0;
177		if (! empty ( $overlay )) {
178			foreach ( $overlay as $data ) {
179				list ( $lat, $lon, $text, $angle, $opacity, $img ) = $data;
180				$rowId ++;
181				$poi .= ", {lat: $lat, lon: $lon, txt: '$text', angle: $angle, opacity: $opacity, img: '$img', rowId: $rowId}";
182
183				if ($this->getConf ( 'displayformat' ) === 'DMS') {
184					$lat = $this->convertLat ( $lat );
185					$lon = $this->convertLon ( $lon );
186				} else {
187					$lat .= 'º';
188					$lon .= 'º';
189				}
190
191				$poitable .= '
192					<tr>
193					<td class="rowId">' . $rowId . '</td>
194					<td class="icon"><img src="' . DOKU_BASE . 'lib/plugins/openlayersmap/icons/' . $img . '" alt="'. substr($img, 0, -4) . $this->getlang('alt_legend_poi').' " /></td>
195					<td class="lat" title="' . $this->getLang ( 'olmapPOIlatTitle' ) . '">' . $lat . '</td>
196					<td class="lon" title="' . $this->getLang ( 'olmapPOIlonTitle' ) . '">' . $lon . '</td>
197					<td class="txt">' . $text . '</td>
198					</tr>';
199			}
200			$poi = substr ( $poi, 2 );
201		}
202		if (! empty ( $gmap ['kmlfile'] )) {
203			$poitable .= '
204					<tr>
205					<td class="rowId"><img src="' . DOKU_BASE . 'lib/plugins/openlayersmap/toolbar/kml_file.png" alt="KML file" /></td>
206					<td class="icon"><img src="' . DOKU_BASE . 'lib/plugins/openlayersmap/toolbar/kml_line.png" alt="' . $this->getlang('alt_legend_kml') .'" /></td>
207					<td class="txt" colspan="3">KML track: ' . $this->getFileName ( $gmap ['kmlfile'] ) . '</td>
208					</tr>';
209		}
210		if (! empty ( $gmap ['gpxfile'] )) {
211			$poitable .= '
212					<tr>
213					<td class="rowId"><img src="' . DOKU_BASE . 'lib/plugins/openlayersmap/toolbar/gpx_file.png" alt="GPX file" /></td>
214					<td class="icon"><img src="' . DOKU_BASE . 'lib/plugins/openlayersmap/toolbar/gpx_line.png" alt="' . $this->getlang('alt_legend_gpx') .'" /></td>
215					<td class="txt" colspan="3">GPX track: ' . $this->getFileName ( $gmap ['gpxfile'] ) . '</td>
216					</tr>';
217		}
218		if (! empty ( $gmap ['geojsonfile'] )) {
219			$poitable .= '
220					<tr>
221					<td class="rowId"><img src="' . DOKU_BASE . 'lib/plugins/openlayersmap/toolbar/geojson_file.png" alt="GeoJSON file" /></td>
222					<td class="icon"><img src="' . DOKU_BASE . 'lib/plugins/openlayersmap/toolbar/geojson_line.png" alt="' . $this->getlang('alt_legend_geojson') .'" /></td>
223					<td class="txt" colspan="3">GeoJSON track: ' . $this->getFileName ( $gmap ['geojsonfile'] ) . '</td>
224					</tr>';
225		}
226
227		$js .= "{mapOpts:{" . $param . ",displayformat:'" . $this->getConf ( 'displayformat' ) . "'},poi:[$poi]};";
228		// unescape the json
229		$poitable = stripslashes ( $poitable );
230
231		return array (
232				$mapid,
233				$js,
234				$mainLat,
235				$mainLon,
236				$poitable,
237				$gmap ['summary'],
238				$imgUrl,
239				$_firstimageID
240		);
241	}
242
243	/**
244	 *
245	 * @see DokuWiki_Syntax_Plugin::render()
246	 */
247	function render($mode, Doku_Renderer $renderer, $data) {
248		// set to true after external scripts tags are written
249		static $initialised = false;
250		// incremented for each map tag in the page source so we can keep track of each map in this page
251		static $mapnumber = 0;
252
253		// dbglog($data, 'olmap::render() data.');
254		list ( $mapid, $param, $mainLat, $mainLon, $poitable, $poitabledesc, $staticImgUrl, $_firstimage ) = $data;
255
256		if ($mode == 'xhtml') {
257			$olscript = '';
258			$olEnable = false;
259			$gscript = '';
260			$gEnable = $this->getConf ( 'enableGoogle' );
261			$stamenEnable = $this->getConf ( 'enableStamen' );
262			$osmEnable = $this->getConf ( 'enableOSM' );
263			$enableBing = $this->getConf ( 'enableBing' );
264
265			$scriptEnable = '';
266			if (! $initialised) {
267				$initialised = true;
268				// render necessary script tags
269				if ($gEnable) {
270					$gscript = '<script type="text/javascript" src="//maps.google.com/maps/api/js?v=3.29&amp;key='.$this->getConf ( 'googleAPIkey' ).'"></script>';
271				}
272				$olscript = '<script type="text/javascript" src="' . DOKU_BASE . 'lib/plugins/openlayersmap/lib/OpenLayers.js"></script>';
273
274				$scriptEnable = '<script type="text/javascript">/*<![CDATA[*/';
275				$scriptEnable .= $olscript ? 'olEnable = true;' : 'olEnable = false;';
276				$scriptEnable .= 'gEnable = ' . ($gEnable ? 'true' : 'false') . ';';
277				$scriptEnable .= 'osmEnable = ' . ($osmEnable ? 'true' : 'false') . ';';
278				$scriptEnable .= 'stamenEnable = ' . ($stamenEnable ? 'true' : 'false') . ';';
279				$scriptEnable .= 'bEnable = ' . ($enableBing ? 'true' : 'false') . ';';
280				$scriptEnable .= 'bApiKey="' . $this->getConf ( 'bingAPIKey' ) . '";';
281				$scriptEnable .= 'tfApiKey="' . $this->getConf ( 'tfApiKey' ) . '";';
282				$scriptEnable .= 'gApiKey="' . $this->getConf ( 'googleAPIkey' ) . '";';
283				$scriptEnable .= '/*!]]>*/</script>';
284			}
285			$renderer->doc .= "$gscript\n$olscript\n$scriptEnable";
286			$renderer->doc .= '<div class="olMapHelp">' . $this->locale_xhtml ( "help" ) . '</div>';
287			if ($this->getConf ( 'enableA11y' )) {
288				$renderer->doc .= '<div id="' . $mapid . '-static" class="olStaticMap">' . p_render ( $mode, p_get_instructions ( $staticImgUrl ), $info ) . '</div>';
289			}
290			$renderer->doc .= '<div id="' . $mapid . '-clearer" class="clearer"><p>&nbsp;</p></div>';
291			if ($this->getConf ( 'enableA11y' )) {
292				// render a table of the POI for the print and a11y presentation, it is hidden using javascript
293				$renderer->doc .= '<div class="olPOItableSpan" id="' . $mapid . '-table-span">
294					<table class="olPOItable" id="' . $mapid . '-table">
295					<caption class="olPOITblCaption">' . $this->getLang ( 'olmapPOItitle' ) . '</caption>
296					<thead class="olPOITblHeader">
297					<tr>
298					<th class="rowId" scope="col">id</th>
299					<th class="icon" scope="col">' . $this->getLang ( 'olmapPOIicon' ) . '</th>
300					<th class="lat" scope="col" title="' . $this->getLang ( 'olmapPOIlatTitle' ) . '">' . $this->getLang ( 'olmapPOIlat' ) . '</th>
301					<th class="lon" scope="col" title="' . $this->getLang ( 'olmapPOIlonTitle' ) . '">' . $this->getLang ( 'olmapPOIlon' ) . '</th>
302					<th class="txt" scope="col">' . $this->getLang ( 'olmapPOItxt' ) . '</th>
303					</tr>
304					</thead>';
305				if ($poitabledesc != '') {
306					$renderer->doc .= '<tfoot class="olPOITblFooter"><tr><td colspan="5">' . $poitabledesc . '</td></tr></tfoot>';
307				}
308				$renderer->doc .= '<tbody class="olPOITblBody">' . $poitable . '</tbody>
309					</table></div>';
310			}
311			// render inline mapscript parts
312			$renderer->doc .= '<script type="text/javascript">/*<![CDATA[*/';
313			$renderer->doc .= " olMapData[$mapnumber] = $param /*!]]>*/</script>";
314			$mapnumber ++;
315			return true;
316		} elseif ($mode == 'metadata') {
317			if (! (($this->dflt ['lat'] == $mainLat) && ($thisdflt ['lon'] == $mainLon))) {
318				// render geo metadata, unless they are the default
319				$renderer->meta ['geo'] ['lat'] = $mainLat;
320				$renderer->meta ['geo'] ['lon'] = $mainLon;
321				if ($geophp = &plugin_load ( 'helper', 'geophp' )) {
322					// if we have the geoPHP helper, add the geohash
323					// fails with older php versions.. $renderer->meta['geo']['geohash'] = (new Point($mainLon,$mainLat))->out('geohash');
324					$p = new Point ( $mainLon, $mainLat );
325					$renderer->meta ['geo'] ['geohash'] = $p->out ( 'geohash' );
326				}
327			}
328
329			if (($this->getConf ( 'enableA11y' )) && (! empty ( $_firstimage ))) {
330				// add map local image into relation/firstimage if not already filled and when it is a local image
331
332				global $ID;
333				$rel = p_get_metadata ( $ID, 'relation', METADATA_RENDER_USING_CACHE );
334				$img = $rel ['firstimage'];
335				if (empty ( $img ) /* || $img == $_firstimage*/){
336					//dbglog ( $_firstimage, 'olmap::render#rendering image relation metadata for _firstimage as $img was empty or the same.' );
337					// This seems to never work; the firstimage entry in the .meta file is empty
338					// $renderer->meta['relation']['firstimage'] = $_firstimage;
339
340					// ... and neither does this; the firstimage entry in the .meta file is empty
341					// $relation = array('relation'=>array('firstimage'=>$_firstimage));
342					// p_set_metadata($ID, $relation, false, false);
343
344					// ... this works
345					$renderer->internalmedia ( $_firstimage, $poitabledesc );
346				}
347			}
348			return true;
349		}
350		return false;
351	}
352
353	/**
354	 * extract parameters for the map from the parameter string
355	 *
356	 * @param string $str_params
357	 *        	string of key="value" pairs
358	 * @return array associative array of parameters key=>value
359	 */
360	private function _extract_params($str_params) {
361		$param = array ();
362		preg_match_all ( '/(\w*)="(.*?)"/us', $str_params, $param, PREG_SET_ORDER );
363		// parse match for instructions, break into key value pairs
364		$gmap = $this->dflt;
365		foreach ( $param as $kvpair ) {
366			list ( $match, $key, $val ) = $kvpair;
367			$key = strtolower ( $key );
368			if (isset ( $gmap [$key] )) {
369				if ($key == 'summary') {
370					// preserve case for summary field
371					$gmap [$key] = $val;
372				} elseif ($key == 'id') {
373					// preserve case for id field
374					$gmap [$key] = $val;
375				} else {
376					$gmap [$key] = strtolower ( $val );
377				}
378			}
379		}
380		return $gmap;
381	}
382
383	/**
384	 * extract overlay points for the map from the wiki syntax data
385	 *
386	 * @param string $str_points
387	 *        	multi-line string of lat,lon,text triplets
388	 * @return array multi-dimensional array of lat,lon,text triplets
389	 */
390	private function _extract_points($str_points) {
391		$point = array ();
392		// preg_match_all('/^([+-]?[0-9].*?),\s*([+-]?[0-9].*?),(.*?),(.*?),(.*?),(.*)$/um', $str_points, $point, PREG_SET_ORDER);
393		/*
394		 * group 1: ([+-]?[0-9]+(?:\.[0-9]*)?) group 2: ([+-]?[0-9]+(?:\.[0-9]*)?) group 3: (.*?) group 4: (.*?) group 5: (.*?) group 6: (.*)
395		 */
396		preg_match_all ( '/^([+-]?[0-9]+(?:\.[0-9]*)?),\s*([+-]?[0-9]+(?:\.[0-9]*)?),(.*?),(.*?),(.*?),(.*)$/um', $str_points, $point, PREG_SET_ORDER );
397		// create poi array
398		$overlay = array ();
399		foreach ( $point as $pt ) {
400			list ( $match, $lat, $lon, $angle, $opacity, $img, $text ) = $pt;
401			$lat = is_numeric ( $lat ) ? $lat : 0;
402			$lon = is_numeric ( $lon ) ? $lon : 0;
403			$angle = is_numeric ( $angle ) ? $angle : 0;
404			$opacity = is_numeric ( $opacity ) ? $opacity : 0.8;
405			// TODO validate using exist & set up default img?
406			$img = trim ( $img );
407			$text = p_get_instructions ( $text );
408			// dbg ( $text );
409			$text = p_render ( "xhtml", $text, $info );
410			// dbg ( $text );
411			$text = addslashes ( str_replace ( "\n", "", $text ) );
412			$overlay [] = array (
413					$lat,
414					$lon,
415					$text,
416					$angle,
417					$opacity,
418					$img
419			);
420		}
421		return $overlay;
422	}
423
424	/**
425	 * Create a MapQuest static map API image url.
426	 *
427	 * @param array $gmap
428	 * @param array $overlay
429	 */
430	 /*
431	private function _getMapQuest($gmap, $overlay) {
432		$sUrl = $this->getConf ( 'iconUrlOverload' );
433		if (! $sUrl) {
434			$sUrl = DOKU_URL;
435		}
436		switch ($gmap ['baselyr']) {
437			case 'mapquest hybrid' :
438				$maptype = 'hyb';
439				break;
440			case 'mapquest sat' :
441				// because sat coverage is very limited use 'hyb' instead of 'sat' so we don't get a blank map
442				$maptype = 'hyb';
443				break;
444			case 'mapquest road' :
445			default :
446				$maptype = 'map';
447				break;
448		}
449		$imgUrl = "http://open.mapquestapi.com/staticmap/v4/getmap?declutter=true&";
450		if (count ( $overlay ) < 1) {
451			$imgUrl .= "?center=" . $gmap ['lat'] . "," . $gmap ['lon'];
452			// max level for mapquest is 16
453			if ($gmap ['zoom'] > 16) {
454				$imgUrl .= "&zoom=16";
455			} else {
456				$imgUrl .= "&zoom=" . $gmap ['zoom'];
457			}
458		}
459		// use bestfit instead of center/zoom, needs upperleft/lowerright corners
460		// $bbox=$this->_calcBBOX($overlay, $gmap['lat'], $gmap['lon']);
461		// $imgUrl .= "bestfit=".$bbox['minlat'].",".$bbox['maxlon'].",".$bbox['maxlat'].",".$bbox['minlon'];
462
463		// TODO declutter option works well for square maps but not for rectangular, maybe compensate for that or compensate the mbr..
464		$imgUrl .= "&size=" . str_replace ( "px", "", $gmap ['width'] ) . "," . str_replace ( "px", "", $gmap ['height'] );
465
466		// TODO mapquest allows using one image url with a multiplier $NUMBER eg:
467		// $NUMBER = 2
468		// $imgUrl .= DOKU_URL."/".DOKU_PLUGIN."/".getPluginName()."/icons/".$img.",$NUMBER,C,".$lat1.",".$lon1.",0,0,0,0,C,".$lat2.",".$lon2.",0,0,0,0";
469		if (! empty ( $overlay )) {
470			$imgUrl .= "&xis=";
471			foreach ( $overlay as $data ) {
472				list ( $lat, $lon, $text, $angle, $opacity, $img ) = $data;
473				// $imgUrl .= $sUrl."lib/plugins/openlayersmap/icons/".$img.",1,C,".$lat.",".$lon.",0,0,0,0,";
474				$imgUrl .= $sUrl . "lib/plugins/openlayersmap/icons/" . $img . ",1,C," . $lat . "," . $lon . ",";
475			}
476			$imgUrl = substr ( $imgUrl, 0, - 1 );
477		}
478		$imgUrl .= "&imageType=png&type=" . $maptype;
479		$imgUrl .= "&key=".$this->getConf ( 'mapquestAPIKey' );
480		// dbglog($imgUrl,'syntax_plugin_openlayersmap_olmap::_getMapQuest: MapQuest image url is:');
481		return $imgUrl;
482	}
483	*/
484
485	/**
486	 * Create a Google maps static image url w/ the poi.
487	 *
488	 * @param array $gmap
489	 * @param array $overlay
490	 */
491	private function _getGoogle($gmap, $overlay) {
492		$sUrl = $this->getConf ( 'iconUrlOverload' );
493		if (! $sUrl) {
494			$sUrl = DOKU_URL;
495		}
496		switch ($gmap ['baselyr']) {
497			case 'google hybrid' :
498				$maptype = 'hybrid';
499				break;
500			case 'google sat' :
501				$maptype = 'satellite';
502				break;
503			case 'terrain' :
504			case 'google relief' :
505				$maptype = 'terrain';
506				break;
507			case 'google road' :
508			default :
509				$maptype = 'roadmap';
510				break;
511		}
512		// TODO maybe use viewport / visible instead of center/zoom,
513		// see: https://developers.google.com/maps/documentation/staticmaps/index#Viewports
514		// 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
515		$imgUrl = "http://maps.googleapis.com/maps/api/staticmap?";
516		$imgUrl .= "&size=" . str_replace ( "px", "", $gmap ['width'] ) . "x" . str_replace ( "px", "", $gmap ['height'] );
517		//if (!$this->getConf( 'autoZoomMap')) { // no need for center & zoom params }
518		$imgUrl .= "&center=" . $gmap ['lat'] . "," . $gmap ['lon'];
519		// max is 21 (== building scale), but that's overkill..
520		if ($gmap ['zoom'] > 17) {
521			$imgUrl .= "&zoom=17";
522		} else {
523			$imgUrl .= "&zoom=" . $gmap ['zoom'];
524		}
525		if (! empty ( $overlay )) {
526			$rowId = 0;
527			foreach ( $overlay as $data ) {
528				list ( $lat, $lon, $text, $angle, $opacity, $img ) = $data;
529				$imgUrl .= "&markers=icon%3a" . $sUrl . "lib/plugins/openlayersmap/icons/" . $img . "%7c" . $lat . "," . $lon . "%7clabel%3a" . ++ $rowId;
530			}
531		}
532		$imgUrl .= "&format=png&maptype=" . $maptype;
533		global $conf;
534		$imgUrl .= "&language=" . $conf ['lang'];
535		if ($this->getConf( 'googleAPIkey' )) {
536			$imgUrl .= "&key=" . $this->getConf( 'googleAPIkey' );
537		}
538		// dbglog($imgUrl,'syntax_plugin_openlayersmap_olmap::_getGoogle: Google image url is:');
539		return $imgUrl;
540	}
541
542	/**
543	 * Create a Bing maps static image url w/ the poi.
544	 *
545	 * @param array $gmap
546	 * @param array $overlay
547	 */
548	private function _getBing($gmap, $overlay) {
549		switch ($gmap ['baselyr']) {
550			case 've hybrid' :
551			case 'bing hybrid' :
552				$maptype = 'AerialWithLabels';
553				break;
554			case 've sat' :
555			case 'bing sat' :
556				$maptype = 'Aerial';
557				break;
558			case 've normal' :
559			case 've road' :
560			case 've' :
561			case 'bing road' :
562			default :
563				$maptype = 'Road';
564				break;
565		}
566		$imgUrl = "http://dev.virtualearth.net/REST/v1/Imagery/Map/" . $maptype;// . "/";
567		if ($this->getConf ( 'autoZoomMap' )) {
568			$bbox = $this->_calcBBOX ( $overlay, $gmap ['lat'], $gmap ['lon'] );
569			//$imgUrl .= "?ma=" . $bbox ['minlat'] . "," . $bbox ['minlon'] . "," . $bbox ['maxlat'] . "," . $bbox ['maxlon'];
570			$imgUrl .= "?ma=" . $bbox ['minlat'] . "%2C" . $bbox ['minlon'] . "%2C" . $bbox ['maxlat'] . "%2C" . $bbox ['maxlon'];
571			$imgUrl .= "&dcl=1";
572		}
573		if (strpos ( $imgUrl, "?" ) === false)
574			$imgUrl .= "?";
575
576		//$imgUrl .= "&ms=" . str_replace ( "px", "", $gmap ['width'] ) . "," . str_replace ( "px", "", $gmap ['height'] );
577		$imgUrl .= "&ms=" . str_replace ( "px", "", $gmap ['width'] ) . "%2C" . str_replace ( "px", "", $gmap ['height'] );
578		$imgUrl .= "&key=" . $this->getConf ( 'bingAPIKey' );
579		if (! empty ( $overlay )) {
580			$rowId = 0;
581			foreach ( $overlay as $data ) {
582				list ( $lat, $lon, $text, $angle, $opacity, $img ) = $data;
583				// TODO icon style lookup, see: http://msdn.microsoft.com/en-us/library/ff701719.aspx for iconStyle
584				$iconStyle = 32;
585				$rowId ++;
586				// NOTE: the max number of pushpins is 18! or we have to use POST (http://msdn.microsoft.com/en-us/library/ff701724.aspx)
587				if ($rowId == 18) {
588					break;
589				}
590				//$imgUrl .= "&pp=$lat,$lon;$iconStyle;$rowId";
591				$imgUrl .= "&pp=$lat%2C$lon%3B$iconStyle%3B$rowId";
592
593			}
594		}
595		global $conf;
596		$imgUrl .= "&fmt=png";
597		$imgUrl .= "&c=" . $conf ['lang'];
598		// dbglog($imgUrl,'syntax_plugin_openlayersmap_olmap::_getBing: bing image url is:');
599		return $imgUrl;
600	}
601
602	/**
603	 * Create a static OSM map image url w/ the poi from http://staticmap.openstreetmap.de (staticMapLite)
604	 * use http://staticmap.openstreetmap.de "staticMapLite" or a local version
605	 *
606	 * @param array $gmap
607	 * @param array $overlay
608	 *
609	 * @todo implementation for http://ojw.dev.openstreetmap.org/StaticMapDev/
610	 */
611	private function _getStaticOSM($gmap, $overlay) {
612		global $conf;
613
614		if ($this->getConf ( 'optionStaticMapGenerator' ) == 'local') {
615			// using local basemap composer
616			if (! $myMap = &plugin_load ( 'helper', 'openlayersmap_staticmap' )) {
617				dbglog ( $myMap, 'syntax_plugin_openlayersmap_olmap::_getStaticOSM: openlayersmap_staticmap plugin is not available.' );
618			}
619			if (! $geophp = &plugin_load ( 'helper', 'geophp' )) {
620				dbglog ( $geophp, 'syntax_plugin_openlayersmap_olmap::_getStaticOSM: geophp plugin is not available.' );
621			}
622			$size = str_replace ( "px", "", $gmap ['width'] ) . "x" . str_replace ( "px", "", $gmap ['height'] );
623
624			$markers = array();
625			if (! empty ( $overlay )) {
626				foreach ( $overlay as $data ) {
627					list ( $lat, $lon, $text, $angle, $opacity, $img ) = $data;
628					$iconStyle = substr ( $img, 0, strlen ( $img ) - 4 );
629					$markers [] = array (
630							'lat' => $lat,
631							'lon' => $lon,
632							'type' => $iconStyle
633					);
634				}
635			}
636
637			$apikey = '';
638			switch ($gmap ['baselyr']) {
639				case 'mapnik' :
640				case 'openstreetmap' :
641					$maptype = 'openstreetmap';
642					break;
643				case 'transport' :
644					$maptype = 'transport';
645					$apikey = $this->getConf ( 'tfApiKey' );
646					break;
647				case 'landscape' :
648					$maptype = 'landscape';
649					$apikey = $this->getConf ( 'tfApiKey' );
650					break;
651				case 'outdoors' :
652					$maptype = 'outdoors';
653					$apikey = $this->getConf ( 'tfApiKey' );
654					break;
655				case 'cycle map' :
656					$maptype = 'cycle';
657					$apikey = $this->getConf ( 'tfApiKey' );
658					break;
659				case 'hike and bike map' :
660					$maptype = 'hikeandbike';
661					break;
662				case 'mapquest hybrid' :
663				case 'mapquest road' :
664				case 'mapquest sat' :
665					$maptype = 'mapquest';
666					break;
667				default :
668					$maptype = '';
669					break;
670			}
671
672			$result = $myMap->getMap ( $gmap ['lat'], $gmap ['lon'], $gmap ['zoom'], $size, $maptype, $markers, $gmap ['gpxfile'], $gmap ['kmlfile'], $gmap ['geojsonfile'] );
673		} else {
674			// using external basemap composer
675
676			// http://staticmap.openstreetmap.de/staticmap.php?center=47.000622235634,10.117187497601&zoom=5&size=500x350
677			// &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
678			$imgUrl = "http://staticmap.openstreetmap.de/staticmap.php";
679			$imgUrl .= "?center=" . $gmap ['lat'] . "," . $gmap ['lon'];
680			$imgUrl .= "&size=" . str_replace ( "px", "", $gmap ['width'] ) . "x" . str_replace ( "px", "", $gmap ['height'] );
681
682			if ($gmap ['zoom'] > 16) {
683				// actually this could even be 18, but that seems overkill
684				$imgUrl .= "&zoom=16";
685			} else {
686				$imgUrl .= "&zoom=" . $gmap ['zoom'];
687			}
688
689			if (! empty ( $overlay )) {
690				$rowId = 0;
691				$imgUrl .= "&markers=";
692				foreach ( $overlay as $data ) {
693					list ( $lat, $lon, $text, $angle, $opacity, $img ) = $data;
694					$rowId ++;
695					$iconStyle = "lightblue$rowId";
696					$imgUrl .= "$lat,$lon,$iconStyle%7c";
697				}
698				$imgUrl = substr ( $imgUrl, 0, - 3 );
699			}
700
701			$result = $imgUrl;
702		}
703		// dbglog ( $result, 'syntax_plugin_openlayersmap_olmap::_getStaticOSM: osm image url is:' );
704		return $result;
705	}
706
707	/**
708	 * Calculate the minimum bbox for a start location + poi.
709	 *
710	 * @param array $overlay
711	 *        	multi-dimensional array of array($lat, $lon, $text, $angle, $opacity, $img)
712	 * @param float $lat
713	 *        	latitude for map center
714	 * @param float $lon
715	 *        	longitude for map center
716	 * @return multitype:float array describing the mbr and center point
717	 */
718	private function _calcBBOX($overlay, $lat, $lon) {
719		$lats = array($lat);
720		$lons = array($lon);
721		foreach ( $overlay as $data ) {
722			list ( $lat, $lon, $text, $angle, $opacity, $img ) = $data;
723			$lats [] = $lat;
724			$lons [] = $lon;
725		}
726		sort ( $lats );
727		sort ( $lons );
728		// TODO: make edge/wrap around cases work
729		$centerlat = $lats [0] + ($lats [count ( $lats ) - 1] - $lats [0]);
730		$centerlon = $lons [0] + ($lons [count ( $lats ) - 1] - $lons [0]);
731		return array (
732				'minlat' => $lats [0],
733				'minlon' => $lons [0],
734				'maxlat' => $lats [count ( $lats ) - 1],
735				'maxlon' => $lons [count ( $lats ) - 1],
736				'centerlat' => $centerlat,
737				'centerlon' => $centerlon
738		);
739	}
740
741	/**
742	 * Figures out the base filename of a media path.
743	 *
744	 * @param String $mediaLink
745	 */
746	private function getFileName($mediaLink) {
747		$mediaLink = str_replace ( '[[', '', $mediaLink );
748		$mediaLink = str_replace ( ']]', '', $mediaLink );
749		$mediaLink = substr ( $mediaLink, 0, - 4 );
750		$parts = explode ( ':', $mediaLink );
751		$mediaLink = end ( $parts );
752		return str_replace ( '_', ' ', $mediaLink );
753	}
754
755	/**
756	 * Convert decimal degrees to degrees, minutes, seconds format
757	 *
758	 * @todo move this into a shared library
759	 * @param float $decimaldegrees
760	 * @return string dms
761	 */
762	private function _convertDDtoDMS($decimaldegrees) {
763		$dms = floor ( $decimaldegrees );
764		$secs = ($decimaldegrees - $dms) * 3600;
765		$min = floor ( $secs / 60 );
766		$sec = round ( $secs - ($min * 60), 3 );
767		$dms .= 'º' . $min . '\'' . $sec . '"';
768		return $dms;
769	}
770
771	/**
772	 * convert latitude in decimal degrees to DMS+hemisphere.
773	 *
774	 * @todo move this into a shared library
775	 * @param float $decimaldegrees
776	 * @return string
777	 */
778	private function convertLat($decimaldegrees) {
779		if (strpos ( $decimaldegrees, '-' ) !== false) {
780			$latPos = "S";
781		} else {
782			$latPos = "N";
783		}
784		$dms = $this->_convertDDtoDMS ( abs ( floatval ( $decimaldegrees ) ) );
785		return hsc ( $dms . $latPos );
786	}
787
788	/**
789	 * convert longitude in decimal degrees to DMS+hemisphere.
790	 *
791	 * @todo move this into a shared library
792	 * @param float $decimaldegrees
793	 * @return string
794	 */
795	private function convertLon($decimaldegrees) {
796		if (strpos ( $decimaldegrees, '-' ) !== false) {
797			$lonPos = "W";
798		} else {
799			$lonPos = "E";
800		}
801		$dms = $this->_convertDDtoDMS ( abs ( floatval ( $decimaldegrees ) ) );
802		return hsc ( $dms . $lonPos );
803	}
804}
805