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