Passed
Pull Request — master (#28)
by Mark
02:13
created

syntax/olmap.php (1 issue)

Labels
Severity
1
<?php
2
/*
3
 * Copyright (c) 2008-2021 Mark C. Prins <[email protected]>
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
use geoPHP\Geometry\Point;
0 ignored issues
show
The type geoPHP\Geometry\Point was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

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