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