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