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