xref: /plugin/openlayersmap/script.js (revision 28fa787d4f47d4051c54d0ba2d8467dbd25e630f)
1/*
2 * Copyright (c) 2008-2022 Mark C. Prins <mprins@users.sf.net>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17
18/**
19 * Test for css support in the browser by sniffing for a css class we added
20 * using javascript added by the action plugin; this is an edge case because
21 * browsers that support javascript generally support css as well.
22 *
23 * @returns {Boolean} true when the browser supports css (and implicitly
24 *          javascript)
25 */
26function olTestCSSsupport() {
27    return (jQuery('.olCSSsupported').length > 0);
28}
29
30/**
31 * Creates a DocumentFragment to insert into the dom.
32 *
33 * @param mapid
34 *            id for the map div
35 * @param width
36 *            width for the map div
37 * @param height
38 *            height for the map div
39 * @returns a {DocumentFragment} element that can be injected into the dom
40 */
41function olCreateMaptag(mapid, width, height) {
42    const // fragment
43        frag = document.createDocumentFragment(),
44        // temp node
45        temp = document.createElement('div');
46    temp.innerHTML = '<div id="' + mapid + '-olContainer" class="olContainer olWebOnly">'
47        // map
48        + '<div id="' + mapid + '" tabindex="0" style="width:' + width + ';height:' + height + ';" class="olMap"></div>'
49        + '</div>';
50    while (temp.firstChild) {
51        frag.appendChild(temp.firstChild);
52    }
53    return frag;
54}
55
56/**
57 * Create the map based on the params given.
58 *
59 * @param mapOpts {Object}
60 *            mapOpts MapOptions hash {id:'olmap', width:500px, height:500px,
61 *            lat:6710200, lon:506500, zoom:13, controls:1,
62 *            baselyr:'', kmlfile:'', gpxfile:'', geojsonfile,
63 *            summary:''}
64 * @param poi {Array}
65 *            OLmapPOI array with POI's [ {lat:6710300,lon:506000,txt:'instap
66 *            punt',angle:180,opacity:.9,img:'', rowId:n},... ]);
67 *
68 * @return {ol.Map} the created map
69 */
70function createMap(mapOpts, poi) {
71
72    // const mapOpts = olMapData[0].mapOpts;
73    // const poi = olMapData[0].poi;
74
75    if (!olEnable) {
76        return null;
77    }
78    if (!olTestCSSsupport()) {
79        olEnable = false;
80        return null;
81    }
82
83    // find map element location
84    const cleartag = document.getElementById(mapOpts.id + '-clearer');
85    if (cleartag === null) {
86        return null;
87    }
88    // create map element and add to document
89    const fragment = olCreateMaptag(mapOpts.id, mapOpts.width, mapOpts.height);
90    cleartag.parentNode.insertBefore(fragment, cleartag);
91
92    /** dynamic map extent. */
93    let extent = ol.extent.createEmpty();
94    let overlayGroup = new ol.layer.Group({title: 'Overlays', fold: 'open', layers: []});
95    const baseLyrGroup = new ol.layer.Group({'title': 'Base maps', layers: []});
96
97    const map = new ol.Map({
98        target:   document.getElementById(mapOpts.id),
99        layers:   [baseLyrGroup, overlayGroup],
100        view:     new ol.View({
101            center:     ol.proj.transform([mapOpts.lon, mapOpts.lat], 'EPSG:4326', 'EPSG:3857'),
102            zoom:       mapOpts.zoom,
103            projection: 'EPSG:3857'
104        }),
105        controls: [
106            new ol.control.Attribution({
107                collapsible: true,
108                collapsed:   true
109            })
110        ]
111    });
112
113    if (osmEnable) {
114        baseLyrGroup.getLayers().push(
115            new ol.layer.Tile({
116                visible: true,
117                title:   'OSM',
118                type:    'base',
119                source:  new ol.source.OSM({
120                    // can be removed after upgrade to OL 7.1, see https://github.com/openlayers/openlayers/pull/14062
121                    url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png'
122                })
123            }));
124
125        baseLyrGroup.getLayers().push(
126            new ol.layer.Tile({
127                visible: mapOpts.baselyr === "opentopomap",
128                title:   'opentopomap',
129                type:    'base',
130                source:  new ol.source.OSM({
131                    url:          'https://{a-c}.tile.opentopomap.org/{z}/{x}/{y}.png',
132                    attributions: 'Data &copy;ODbL <a href="https://openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>, '
133                                      + '<a href="http://viewfinderpanoramas.org" target="_blank">SRTM</a>, '
134                                      + 'style &copy;<a href="https://opentopomap.org/" target="_blank">OpenTopoMap</a>'
135                                      + '(<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)'
136                })
137            }));
138
139        baseLyrGroup.getLayers().push(
140            new ol.layer.Tile({
141                visible: mapOpts.baselyr === "cycle map",
142                title:   'cycle map',
143                type:    'base',
144                source:  new ol.source.OSM({
145                    url:          'https://{a-c}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey=' + tfApiKey,
146                    attributions: 'Data &copy;ODbL <a href="https://openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>, '
147                                      + 'Tiles &copy;<a href="https://www.thunderforest.com/" target="_blank">Thunderforest</a>'
148                                      + '<img src="https://www.thunderforest.com/favicon.ico" alt="Thunderforest logo"/>'
149                })
150            }));
151
152        baseLyrGroup.getLayers().push(
153            new ol.layer.Tile({
154                visible: mapOpts.baselyr === "transport",
155                title:   'transport',
156                type:    'base',
157                source:  new ol.source.OSM({
158                    url:          'https://{a-c}.tile.thunderforest.com/transport/{z}/{x}/{y}.png?apikey=' + tfApiKey,
159                    attributions: 'Data &copy;ODbL <a href="https://openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>, '
160                                      + 'Tiles &copy;<a href="https://www.thunderforest.com/" target="_blank">Thunderforest</a>'
161                                      + '<img src="https://www.thunderforest.com/favicon.ico" alt="Thunderforest logo"/>'
162                })
163            }));
164
165        baseLyrGroup.getLayers().push(
166            new ol.layer.Tile({
167                visible: mapOpts.baselyr === "landscape",
168                title:   'landscape',
169                type:    'base',
170                source:  new ol.source.OSM({
171                    url:          'https://{a-c}.tile.thunderforest.com/landscape/{z}/{x}/{y}.png?apikey=' + tfApiKey,
172                    attributions: 'Data &copy;ODbL <a href="https://openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>, '
173                                      + 'Tiles &copy;<a href="https://www.thunderforest.com/" target="_blank">Thunderforest</a>'
174                                      + '<img src="https://www.thunderforest.com/favicon.ico" alt="Thunderforest logo"/>'
175                })
176            }));
177
178        baseLyrGroup.getLayers().push(
179            new ol.layer.Tile({
180                visible: mapOpts.baselyr === "outdoors",
181                title:   'outdoors',
182                type:    'base',
183                source:  new ol.source.OSM({
184                    url:          'https://{a-c}.tile.thunderforest.com/outdoors/{z}/{x}/{y}.png?apikey=' + tfApiKey,
185                    attributions: 'Data &copy;ODbL <a href="https://openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>, '
186                                      + 'Tiles &copy;<a href="https://www.thunderforest.com/" target="_blank">Thunderforest</a>'
187                                      + '<img src="https://www.thunderforest.com/favicon.ico" alt="Thunderforest logo"/>'
188                })
189            }));
190    }
191
192    if (bEnable && bApiKey !== '') {
193        baseLyrGroup.getLayers().push(
194            new ol.layer.Tile({
195                visible: mapOpts.baselyr === "bing road",
196                title:   'bing road',
197                type:    'base',
198                source:  new ol.source.BingMaps({
199                    key:        bApiKey,
200                    imagerySet: 'Road'
201                })
202            }));
203
204        baseLyrGroup.getLayers().push(
205            new ol.layer.Tile({
206                visible: mapOpts.baselyr === "bing sat",
207                title:   'bing sat',
208                type:    'base',
209                source:  new ol.source.BingMaps({
210                    key:        bApiKey,
211                    imagerySet: 'Aerial'
212                })
213            }));
214
215        baseLyrGroup.getLayers().push(
216            new ol.layer.Tile({
217                visible: mapOpts.baselyr === "bing hybrid",
218                title:   'bing hybrid',
219                type:    'base',
220                source:  new ol.source.BingMaps({
221                    key:        bApiKey,
222                    imagerySet: 'AerialWithLabels'
223                })
224            }));
225    }
226
227    if (stamenEnable) {
228        baseLyrGroup.getLayers().push(
229            new ol.layer.Tile({
230                visible: mapOpts.baselyr === "toner",
231                type:    'base',
232                title:   'toner',
233                source:  new ol.source.Stamen({layer: 'toner'})
234            })
235        );
236
237        baseLyrGroup.getLayers().push(
238            new ol.layer.Tile({
239                visible: mapOpts.baselyr === "terrain",
240                type:    'base',
241                title:   'terrain',
242                source:  new ol.source.Stamen({layer: 'terrain'})
243            })
244        );
245    }
246
247    extent = ol.extent.extend(extent, map.getView().calculateExtent());
248
249    const iconScale = 1.0;
250    const vectorSource = new ol.source.Vector();
251    poi.forEach((p) => {
252        const f = new ol.Feature({
253            geometry:    new ol.geom.Point(ol.proj.fromLonLat([p.lon, p.lat])),
254            description: p.txt,
255            img:         p.img,
256            rowId:       p.rowId,
257            lat:         p.lat,
258            lon:         p.lon,
259            angle:       p.angle,
260            opacity:     p.opacity,
261            alt:         p.img.substring(0, p.img.lastIndexOf("."))
262        });
263        f.setId(p.rowId);
264        vectorSource.addFeature(f);
265    });
266
267    const vectorLayer = new ol.layer.Vector({
268        title:   'POI',
269        visible: true,
270        source:  vectorSource,
271        style(feature, resolution) {
272            const img = feature.get('img');
273            const opacity = feature.get('opacity');
274            const angle = feature.get('angle');
275            const text = feature.get('rowId');
276
277            return new ol.style.Style({
278                image: new ol.style.Icon({
279                    src:         `${DOKU_BASE}lib/plugins/openlayersmap/icons/${img}`,
280                    crossOrigin: '',
281                    opacity:     opacity,
282                    scale:       iconScale,
283                    rotation:    angle * Math.PI / 180,
284                }),
285                text:  new ol.style.Text({
286                    text:           `${text}`,
287                    textAlign:      'center',
288                    textBaseline:   'middle',
289                    offsetX:        (8 + 4) * iconScale,
290                    offsetY:        -8 * iconScale,
291                    scale:          iconScale,
292                    fill:           new ol.style.Fill({color: 'rgb(0,0,0)'}),
293                    font:           'bold 1em monospace',
294                    backgroundFill: new ol.style.Fill({color: 'rgba(255,255,255,.4)'})
295                })
296            });
297        }
298    });
299    overlayGroup.getLayers().push(vectorLayer);
300    if (mapOpts.autozoom) {
301        extent = ol.extent.extend(extent, vectorSource.getExtent());
302        map.getView().fit(extent);
303    }
304
305    if (mapOpts.controls === 1) {
306        map.addControl(new ol.control.Zoom());
307        map.addControl(new ol.control.ScaleLine({bar: true, text: true}));
308        map.addControl(new ol.control.MousePosition({
309            coordinateFormat: ol.coordinate.createStringXY(4), projection: 'EPSG:4326',
310        }));
311        map.addControl(new ol.control.FullScreen({
312            label: '✈'
313        }));
314        map.addControl(new ol.control.OverviewMap({
315            label:  '+',
316            layers: [new ol.layer.Tile({
317                source: new ol.source.OSM()
318            })]
319        }));
320        map.addControl(new ol.control.LayerSwitcher({
321            activationMode: 'click',
322            label:          '\u2630',
323            collapseLabel:  '\u00BB',
324        }));
325    }
326
327    if (mapOpts.kmlfile.length > 0) {
328        try {
329            const kmlSource = new ol.source.Vector({
330                url:    DOKU_BASE + "lib/exe/fetch.php?media=" + mapOpts.kmlfile,
331                format: new ol.format.KML(),
332            });
333            overlayGroup.getLayers().push(new ol.layer.Vector({title: 'KML file', visible: true, source: kmlSource}));
334
335            if (mapOpts.autozoom) {
336                kmlSource.once('change', function () {
337                    extent = ol.extent.extend(extent, kmlSource.getExtent());
338                    map.getView().fit(extent);
339                });
340            }
341        } catch (e) {
342            console.error(e);
343        }
344    }
345
346    if (mapOpts.geojsonfile.length > 0) {
347        try {
348            // these are the same colour as in StaticMap#drawJSON()
349            const geoJsonStyle = {
350                'Point':           new ol.style.Style({
351                    image: new ol.style.Circle({
352                        fill:   new ol.style.Fill({
353                            color: 'rgba(255,0,255,0.4)',
354                        }),
355                        radius: 5,
356                        stroke: new ol.style.Stroke({
357                            color: 'rgba(255,0,255,0.9)',
358                            width: 1,
359                        }),
360                    }),
361                }),
362                'LineString':      new ol.style.Style({
363                    stroke: new ol.style.Stroke({
364                        color: 'rgba(255,0,255,0.9)',
365                        width: 3,
366                    }),
367                }),
368                'MultiLineString': new ol.style.Style({
369                    stroke: new ol.style.Stroke({
370                        color: 'rgba(255,0,255,0.9)',
371                        width: 3,
372                    }),
373                }),
374                'Polygon':         new ol.style.Style({
375                    stroke: new ol.style.Stroke({
376                        color: 'rgba(255,0,255,0.9)',
377                        width: 3,
378                    }),
379                    fill:   new ol.style.Fill({
380                        color: 'rgba(255,0,255,0.4)',
381                    }),
382                }),
383                'MultiPolygon':    new ol.style.Style({
384                    stroke: new ol.style.Stroke({
385                        color: 'rgba(255,0,255,0.9)',
386                        width: 3,
387                    }),
388                    fill:   new ol.style.Fill({
389                        color: 'rgba(255,0,255,0.4)',
390                    }),
391                }),
392            };
393            const geoJsonSource = new ol.source.Vector({
394                url:    DOKU_BASE + "lib/exe/fetch.php?media=" + mapOpts.geojsonfile,
395                format: new ol.format.GeoJSON(),
396            });
397            overlayGroup.getLayers().push(new ol.layer.Vector({
398                title: 'GeoJSON file', visible: true, source: geoJsonSource,
399                style: function (feature) {
400                    return geoJsonStyle[feature.getGeometry().getType()];
401                },
402            }));
403
404            if (mapOpts.autozoom) {
405                geoJsonSource.once('change', function () {
406                    extent = ol.extent.extend(extent, geoJsonSource.getExtent());
407                    map.getView().fit(extent);
408                });
409            }
410        } catch (e) {
411            console.error(e);
412        }
413    }
414
415    if (mapOpts.gpxfile.length > 0) {
416        try {
417            // these are the same colour as in StaticMap#drawGPX()
418            const gpxJsonStyle = {
419                'Point':           new ol.style.Style({
420                    image: new ol.style.Circle({
421                        fill:   new ol.style.Fill({
422                            color: 'rgba(0,0,255,0.4)',
423                        }),
424                        radius: 5,
425                        stroke: new ol.style.Stroke({
426                            color: 'rgba(0,0,255,0.9)',
427                            width: 1,
428                        }),
429                    }),
430                }),
431                'LineString':      new ol.style.Style({
432                    stroke: new ol.style.Stroke({
433                        color: 'rgba(0,0,255,0.9)',
434                        width: 3,
435                    }),
436                }),
437                'MultiLineString': new ol.style.Style({
438                    stroke: new ol.style.Stroke({
439                        color: 'rgba(0,0,255,0.9)',
440                        width: 3,
441                    }),
442                }),
443            };
444            const gpxSource = new ol.source.Vector({
445                url:    DOKU_BASE + "lib/exe/fetch.php?media=" + mapOpts.gpxfile,
446                format: new ol.format.GPX(),
447            });
448            overlayGroup.getLayers().push(new ol.layer.Vector({
449                title: 'GPS track', visible: true, source: gpxSource,
450                style: function (feature) {
451                    return gpxJsonStyle[feature.getGeometry().getType()];
452                },
453            }));
454
455            if (mapOpts.autozoom) {
456                gpxSource.once('change', function () {
457                    extent = ol.extent.extend(extent, gpxSource.getExtent());
458                    map.getView().fit(extent);
459                });
460            }
461        } catch (e) {
462            console.error(e);
463        }
464    }
465
466    const container = document.getElementById('popup');
467    const content = document.getElementById('popup-content');
468    const closer = document.getElementById('popup-closer');
469
470    const overlay = new ol.Overlay({
471        element:     container,
472        positioning: 'center-center',
473        stopEvent:   true,
474        autoPan:     {
475            animation: {
476                duration: 250,
477            }
478        },
479    });
480    map.addOverlay(overlay);
481
482    /**
483     * Add a click handler to hide the popup.
484     * @return {boolean} Don't follow the href.
485     */
486    closer.onclick = function () {
487        overlay.setPosition(undefined);
488        closer.blur();
489        return false;
490    };
491
492    // display popup on click
493    map.on('singleclick', function (evt) {
494        const selFeature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
495            return feature;
496        });
497        if (selFeature) {
498            overlay.setPosition(evt.coordinate);
499
500            let pContent = '<div class="spacer">&nbsp;</div>';
501            // let locDesc = '';
502
503            if (selFeature.get('rowId') !== undefined) {
504                pContent += '<span class="rowId">' + selFeature.get('rowId') + ': </span>';
505            }
506            if (selFeature.get('name') !== undefined) {
507                pContent += '<span class="txt">' + selFeature.get('name') + '</span>';
508                // locDesc = selFeature.get('name');
509                // TODO strip <p> tag from locDesc
510                // locDesc = selFeature.get('name').split(/\s+/).slice(0,2).join('+');
511            }
512            if (selFeature.get('ele') !== undefined) {
513                pContent += '<div class="ele">elevation: ' + selFeature.get('ele') + '</div>';
514            }
515            if (selFeature.get('type') !== undefined) {
516                pContent += '<div>' + selFeature.get('type') + '</div>';
517            }
518            if (selFeature.get('time') !== undefined) {
519                pContent += '<div class="time">time: ' + selFeature.get('time') + '</div>';
520            }
521            if (selFeature.get('description') !== undefined) {
522                pContent += '<div class="desc">' + selFeature.get('description') + '</div>';
523            }
524            if (selFeature.get('img') !== undefined) {
525                const _alt = selFeature.get('alt');
526                pContent += '<div class="coord" title="lat;lon">' +
527                    '<img alt="' + _alt + '" src="' + DOKU_BASE + 'lib/plugins/openlayersmap/icons/' + selFeature.get('img') +
528                    '" width="16" height="16" ' + 'style="transform:rotate(' + selFeature.get('angle') + 'deg)" />&nbsp;' +
529                    '<a href="geo:' + selFeature.get('lat') + ',' + selFeature.get('lon') + '?q=' + selFeature.get('lat') +
530                    ',' + selFeature.get('lon') + '(' + selFeature.get('alt') + ')" title="Open in navigation app">' +
531                    ol.coordinate.format([selFeature.get('lon'), selFeature.get('lat')], '{x}º; {y}º', 4) + '</a></div>';
532            }
533            content.innerHTML = pContent;
534        } else {
535            // do nothing...
536        }
537    });
538
539    // change mouse cursor when over marker
540    map.on('pointermove', function (e) {
541        const pixel = map.getEventPixel(e.originalEvent);
542        const hit = map.hasFeatureAtPixel(pixel);
543        map.getTarget().style.cursor = hit ? 'pointer' : '';
544    });
545
546    return map;
547}
548
549/**
550 * add layers to the map based on the olMapOverlays object.
551 */
552function olovAddToMap() {
553    for (const key in olMapOverlays) {
554        const overlay = olMapOverlays[key];
555        const m = olMaps[overlay.id];
556
557        switch (overlay.type) {
558            case 'osm':
559                m.addLayer(new ol.layer.Tile({
560                    title:   overlay.name,
561                    visible: (overlay.visible).toLowerCase() === 'true',
562                    opacity: parseFloat(overlay.opacity),
563                    source:  new ol.source.OSM({
564                        url:          overlay.url,
565                        crossOrigin:  'Anonymous',
566                        attributions: overlay.attribution
567                    })
568                }));
569                break;
570            case 'wms':
571                m.addLayer(new ol.layer.Image({
572                    title:   overlay.name,
573                    opacity: parseFloat(overlay.opacity),
574                    visible: (overlay.visible).toLowerCase() === 'true',
575                    source:  new ol.source.ImageWMS({
576                        url:          overlay.url,
577                        params:       {
578                            'LAYERS':      overlay.layers,
579                            'VERSION':     overlay.version,
580                            'TRANSPARENT': overlay.transparent,
581                            'FORMAT':      overlay.format
582                        },
583                        ratio:        1,
584                        crossOrigin:  'Anonymous',
585                        attributions: overlay.attribution
586                    })
587                }));
588                break;
589            case 'ags':
590                m.addLayer(new ol.layer.Image({
591                    title:   overlay.name,
592                    opacity: parseFloat(overlay.opacity),
593                    visible: (overlay.visible).toLowerCase() === 'true',
594                    source:  new ol.source.ImageArcGISRest({
595                        url:          overlay.url,
596                        params:       {
597                            'LAYERS':      overlay.layers,
598                            'TRANSPARENT': overlay.transparent,
599                            'FORMAT':      overlay.format
600                        },
601                        ratio:        1,
602                        crossOrigin:  'Anonymous',
603                        attributions: overlay.attribution
604                    })
605                }));
606                break;
607            // case 'mapillary':
608            //     var mUrl = 'http://api.mapillary.com/v1/im/search?';
609            //     if (overlay.skey !== '') {
610            //         mUrl = 'http://api.mapillary.com/v1/im/sequence?';
611            //     }
612            //     var mLyr = new OpenLayers.Layer.Vector(
613            //         "Mapillary", {
614            //             projection:  new OpenLayers.Projection("EPSG:4326"),
615            //             strategies:  [new OpenLayers.Strategy.BBOX({
616            //                 ratio:     1.1,
617            //                 resFactor: 1.5
618            //             }) /* ,new OpenLayers.Strategy.Cluster({}) */],
619            //             protocol:    new OpenLayers.Protocol.HTTP({
620            //                 url:            mUrl,
621            //                 format:         new OpenLayers.Format.GeoJSON(),
622            //                 params:         {
623            //                     // default to max. 250 locations
624            //                     'max-results': 250,
625            //                     'geojson':     true,
626            //                     'skey':        overlay.skey
627            //                 },
628            //                 filterToParams: function (filter, params) {
629            //                     if (filter.type === OpenLayers.Filter.Spatial.BBOX) {
630            //                         // override the bbox serialization of
631            //                         // the filter to give the Mapillary
632            //                         // specific bounds
633            //                         params['min-lat'] = filter.value.bottom;
634            //                         params['max-lat'] = filter.value.top;
635            //                         params['min-lon'] = filter.value.left;
636            //                         params['max-lon'] = filter.value.right;
637            //                         // if the width of our bbox width is
638            //                         // less than 0.15 degrees drop the max
639            //                         // results
640            //                         if (filter.value.top - filter.value.bottom < .15) {
641            //                             OpenLayers.Console.debug('dropping max-results parameter, width is: ',
642            //                                 filter.value.top - filter.value.bottom);
643            //                             params['max-results'] = null;
644            //                         }
645            //                     }
646            //                     return params;
647            //                 }
648            //             }),
649            //             styleMap:    new OpenLayers.StyleMap({
650            //                 'default': {
651            //                     cursor:          'help',
652            //                     rotation:        '${ca}',
653            //                     externalGraphic: DOKU_BASE + 'lib/plugins/openlayersmapoverlays/icons/arrow-up-20.png',
654            //                     graphicHeight:   20,
655            //                     graphicWidth:    20,
656            //                 },
657            //                 'select':  {
658            //                     externalGraphic: DOKU_BASE + 'lib/plugins/openlayersmapoverlays/icons/arrow-up-20-select.png',
659            //                     label:           '${location}',
660            //                     fontSize:        '1em',
661            //                     fontFamily:      'monospace',
662            //                     labelXOffset:    '0.5',
663            //                     labelYOffset:    '0.5',
664            //                     labelAlign:      'lb',
665            //                 }
666            //             }),
667            //             attribution: '<a href="http://www.mapillary.com/legal.html">' +
668            //                              '<img src="http://mapillary.com/favicon.ico" ' +
669            //                              'alt="Mapillary" height="16" width="16" />Mapillary (CC-BY-SA)',
670            //             visibility:  (overlay.visible).toLowerCase() == 'true',
671            //         });
672            //     m.addLayer(mLyr);
673            //     selectControl.addLayer(mLyr);
674            //     break;
675            // case 'search':
676            //     m.addLayer(new OpenLayers.Layer.Vector(
677            //         overlay.name,
678            //         overlay.url,
679            //         {
680            //             layers:      overlay.layers,
681            //             version:     overlay.version,
682            //             transparent: overlay.transparent,
683            //             format:      overlay.format
684            //         }, {
685            //             opacity:     parseFloat(overlay.opacity),
686            //             visibility:  (overlay.visible).toLowerCase() == 'true',
687            //             isBaseLayer: !1,
688            //             attribution: overlay.attribution
689            //         }
690            //     ));
691            //     break;
692        }
693    }
694}
695
696/** init. */
697function olInit() {
698    if (olEnable) {
699        // add info window to DOM
700        const frag = document.createDocumentFragment(),
701            temp = document.createElement('div');
702        temp.innerHTML = '<div id="popup" class="olPopup"><a href="#" id="popup-closer" class="olPopupCloseBox"></a><div id="popup-content"></div></div>';
703        while (temp.firstChild) {
704            frag.appendChild(temp.firstChild);
705        }
706        document.body.appendChild(frag);
707
708        let _i = 0;
709        // create the maps in the page
710        for (_i = 0; _i < olMapData.length; _i++) {
711            const _id = olMapData[_i].mapOpts.id;
712            olMaps[_id] = createMap(olMapData[_i].mapOpts, olMapData[_i].poi);
713
714            // set max-width on help pop-over
715            jQuery('#' + _id).parent().parent().find('.olMapHelp').css('max-width', olMapData[_i].mapOpts.width);
716
717            // shrink the map width to fit inside page container
718            const _w = jQuery('#' + _id + '-olContainer').parent().innerWidth();
719            if (parseInt(olMapData[_i].mapOpts.width) > _w) {
720                jQuery('#' + _id).width(_w);
721                jQuery('#' + _id).parent().parent().find('.olMapHelp').width(_w);
722                olMaps[_id].updateSize();
723            }
724        }
725
726        // add overlays
727        olovAddToMap();
728
729        let resizeTimer;
730        jQuery(window).on('resize', function (e) {
731            clearTimeout(resizeTimer);
732            resizeTimer = setTimeout(function () {
733                for (_i = 0; _i < olMapData.length; _i++) {
734                    const _id = olMapData[_i].mapOpts.id;
735                    const _w = jQuery('#' + _id + '-olContainer').parent().innerWidth();
736                    if (parseInt(olMapData[_i].mapOpts.width) > _w) {
737                        jQuery('#' + _id).width(_w);
738                        jQuery('#' + _id).parent().parent().find('.olMapHelp').width(_w);
739                        olMaps[_id].updateSize();
740                    }
741                }
742            }, 250);
743        });
744
745        // hide the table(s) with POI by giving it a print-only style
746        jQuery('.olPOItableSpan').addClass('olPrintOnly');
747        // hide the static map image(s) by giving it a print only style
748        jQuery('.olStaticMap').addClass('olPrintOnly');
749        // add help button with toggle.
750        jQuery('.olWebOnly > .olMap')
751            .prepend(
752                '<div class="olMapHelpButtonDiv">'
753                + '<button onclick="jQuery(\'.olMapHelp\').toggle(500);" class="olMapHelpButton olHasTooltip"><span>'
754                + 'Show or hide help</span>?</button></div>');
755        // toggle to switch dynamic vs. static map
756        jQuery('.olMapHelp').before(
757            '<div class="a11y"><button onclick="jQuery(\'.olPrintOnly\').toggle();jQuery(\'.olWebOnly\').toggle();">'
758            + 'Hide or show the dynamic map</button></div>');
759    }
760}
761
762/**
763 * CSS support flag.
764 *
765 * @type {Boolean}
766 */
767let olCSSEnable = true;
768
769/* register olInit to run with onload event. */
770jQuery(olInit);
771