Passed
Pull Request — master (#10)
by Mark
03:24
created

script.js (20 issues)

1
/*
2
 * Copyright (c) 2022 Mark C. Prins <[email protected]>
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
 */
26 View Code Duplication
function olTestCSSsupport() {
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
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
 */
41
function olCreateMaptag(mapid, width, height) {
42
    var mEl = '<div id="' + mapid + '-olContainer" class="olContainer olWebOnly">'
43
            // map
44
            + '<div id="' + mapid + '" tabindex="0" style="width:' + width + ';height:' + height + ';" class="olMap"></div>'
45
            + '</div>',
46
        // fragment
47
        frag = document.createDocumentFragment(),
48
        // temp node
49
        temp = document.createElement('div');
50
    temp.innerHTML = mEl;
51
    while (temp.firstChild) {
52
        frag.appendChild(temp.firstChild);
53
    }
54
    return frag;
55
}
56
57
/**
58
 * Create the map based on the params given.
59
 *
60
 * @param {Object}
61
 *            mapOpts MapOptions hash {id:'olmap', width:500px, height:500px,
62
 *            lat:6710200, lon:506500, zoom:13, statusbar:1, controls:1,
63
 *            poihoverstyle:1, baselyr:'', kmlfile:'', gpxfile:'', geojsonfile,
64
 *            summary:''}
65
 * @param {Array}
66
 *            OLmapPOI array with POI's [ {lat:6710300,lon:506000,txt:'instap
67
 *            punt',angle:180,opacity:.9,img:'', rowId:n},... ]);
68
 *
69
 * @return {OpenLayers.Map} the created map
70
 */
71
function createMap(mapOpts, poi) {
72
73
    // const mapOpts = olMapData[0].mapOpts;
74
    // const poi = olMapData[0].poi;
75
76
    if (!olEnable) {
0 ignored issues
show
If you intend to check if the variable olEnable is declared in the current environment, consider using typeof olEnable === "undefined" instead. This is safe if the variable is not actually declared.
Loading history...
77
        return;
78
    }
79
    if (!olTestCSSsupport()) {
80
        olEnable = false;
0 ignored issues
show
The local (let) variable olEnable is used before it is defined. This will cause a reference error.
Loading history...
81
        return;
82
    }
83
84
    // find map element location
85
    var cleartag = document.getElementById(mapOpts.id + '-clearer');
86
    if (cleartag === null) {
87
        return;
88
    }
89
    // create map element and add to document
90
    var fragment = olCreateMaptag(mapOpts.id, mapOpts.width, mapOpts.height);
91
    cleartag.parentNode.insertBefore(fragment, cleartag);
92
93
94
    const overlayGroup = new ol.layer.Group({title: 'Overlays', fold: 'open', layers: []});
0 ignored issues
show
The variable ol seems to be never declared. If this is a global, consider adding a /** global: ol */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
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
    });
106
107
    let /**
108
     * Thunderforest API key.
109
     *
110
     * @type {String}
111
     */
112
    tfApiKey = '';
113
    let /**
114
     * OSM tiles flag.
115
     *
116
     * @type {Boolean}
117
     */
118
    osmEnable = true;
119
    if (osmEnable) {
120
        baseLyrGroup.getLayers().push(
121
            new ol.layer.Tile({
122
                visible: true,
123
                title:   'OSM',
124
                type:    'base',
125
                source:  new ol.source.OSM()
126
            }));
127
128
        baseLyrGroup.getLayers().push(
129
            new ol.layer.Tile({
130
                visible: mapOpts.baselyr === "cycle map",
131
                title:   'cycle map',
132
                type:    'base',
133
                source:  new ol.source.OSM({
134
                    url:          'https://{a-c}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey=' + tfApiKey,
135
                    attributions: 'Data &copy;ODbL <a href="https://openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>, '
136
                                      + 'Tiles &copy;<a href="https://www.thunderforest.com/" target="_blank">Thunderforest</a>'
137
                                      + '<img src="https://www.thunderforest.com/favicon.ico" alt="Thunderforest logo"/>'
138
                })
139
            }));
140
141
        baseLyrGroup.getLayers().push(
142
            new ol.layer.Tile({
143
                visible: mapOpts.baselyr === "transport",
144
                title:   'transport',
145
                type:    'base',
146
                source:  new ol.source.OSM({
147
                    url:          'https://{a-c}.tile.thunderforest.com/transport/{z}/{x}/{y}.png?apikey=' + tfApiKey,
148
                    attributions: 'Data &copy;ODbL <a href="https://openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>, '
149
                                      + 'Tiles &copy;<a href="https://www.thunderforest.com/" target="_blank">Thunderforest</a>'
150
                                      + '<img src="https://www.thunderforest.com/favicon.ico" alt="Thunderforest logo"/>'
151
                })
152
            }));
153
154
        baseLyrGroup.getLayers().push(
155
            new ol.layer.Tile({
156
                visible: mapOpts.baselyr === "landscape",
157
                title:   'landscape',
158
                type:    'base',
159
                source:  new ol.source.OSM({
160
                    url:          'https://{a-c}.tile.thunderforest.com/landscape/{z}/{x}/{y}.png?apikey=' + tfApiKey,
161
                    attributions: 'Data &copy;ODbL <a href="https://openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>, '
162
                                      + 'Tiles &copy;<a href="https://www.thunderforest.com/" target="_blank">Thunderforest</a>'
163
                                      + '<img src="https://www.thunderforest.com/favicon.ico" alt="Thunderforest logo"/>'
164
                })
165
            }));
166
167
        baseLyrGroup.getLayers().push(
168
            new ol.layer.Tile({
169
                visible: mapOpts.baselyr === "outdoors",
170
                title:   'outdoors',
171
                type:    'base',
172
                source:  new ol.source.OSM({
173
                    url:          'https://{a-c}.tile.thunderforest.com/outdoors/{z}/{x}/{y}.png?apikey=' + tfApiKey,
174
                    attributions: 'Data &copy;ODbL <a href="https://openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>, '
175
                                      + 'Tiles &copy;<a href="https://www.thunderforest.com/" target="_blank">Thunderforest</a>'
176
                                      + '<img src="https://www.thunderforest.com/favicon.ico" alt="Thunderforest logo"/>'
177
                })
178
            }));
179
    }
180
181
    let /**
182
     * Bing tiles flag.
183
     *
184
     * @type {Boolean}
185
     */
186
    bEnable = false;
187
    let /**
188
     * Bing API key.
189
     *
190
     * @type {String}
191
     */
192
    bApiKey = '';
193
    if (bEnable && bApiKey !== '') {
194
        baseLyrGroup.getLayers().push(
195
            new ol.layer.Tile({
196
                visible: mapOpts.baselyr === "bing road",
197
                title:   'bing road',
198
                type:    'base',
199
                source:  new ol.source.BingMaps({
200
                    key:        bApiKey,
201
                    imagerySet: 'Road'
202
                })
203
            }));
204
205
        baseLyrGroup.getLayers().push(
206
            new ol.layer.Tile({
207
                visible: mapOpts.baselyr === "bing sat",
208
                title:   'bing sat',
209
                type:    'base',
210
                source:  new ol.source.BingMaps({
211
                    key:        bApiKey,
212
                    imagerySet: 'Aerial'
213
                })
214
            }));
215
216
        baseLyrGroup.getLayers().push(
217
            new ol.layer.Tile({
218
                visible: mapOpts.baselyr === "bing hybrid",
219
                title:   'bing hybrid',
220
                type:    'base',
221
                source:  new ol.source.BingMaps({
222
                    key:        bApiKey,
223
                    imagerySet: 'AerialWithLabels'
224
                })
225
            }));
226
    }
227
228
    let /**
229
     * Stamen tiles flag.
230
     *
231
     * @type {Boolean}
232
     */
233
    stamenEnable = false;
234
    if (stamenEnable) {
235
        baseLyrGroup.getLayers().push(
236
            new ol.layer.Tile({
237
                visible: false,
238
                type:    'base',
239
                title:   'toner',
240
                source:  new ol.source.Stamen({layer: 'toner'})
241
            })
242
        );
243
        baseLyrGroup.getLayers().push(
244
            new ol.layer.Tile({
245
                visible: false,
246
                type:    'base',
247
                title:   'terrain',
248
                source:  new ol.source.Stamen({layer: 'terrain'})
249
            })
250
        );
251
    }
252
253
    map.addControl(new ol.control.ScaleLine({bar: true, text: true}));
254
    map.addControl(new ol.control.MousePosition({
255
        coordinateFormat: ol.coordinate.createStringXY(4), projection: 'EPSG:4326',
256
    }));
257
    map.addControl(new ol.control.FullScreen());
258
    map.addControl(new ol.control.OverviewMap());
259
    map.addControl(new ol.control.LayerSwitcher());
260
261
    const iconScale = 1.5;
262
    const vectorSource = new ol.source.Vector();
263
    poi.forEach((p) => {
264
        const f = new ol.Feature({
0 ignored issues
show
The variable ol seems to be never declared. If this is a global, consider adding a /** global: ol */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
265
            geometry:    new ol.geom.Point(ol.proj.fromLonLat([p.lon, p.lat])),
266
            description: p.txt,
267
            img:         p.img,
268
            rowId:       p.rowId,
269
            lat:         p.lat,
270
            lon:         p.lon,
271
            angle:       p.angle,
272
            alt:         p.img.substring(0, p.img.lastIndexOf("."))
273
        });
274
        f.setId(p.rowId);
275
        f.setStyle(new ol.style.Style({
276
            text:      new ol.style.Text({
277
                text:           "" + p.rowId,
278
                textAlign:      'left',
279
                textBaseline:   'bottom',
280
                offsetX:        8,
281
                offsetY:        -8,
282
                scale:          iconScale,
283
                fill:           new ol.style.Fill({color: 'rgb(0,0,0)'}),
284
                font:           '12px monospace bold',
285
                backgroundFill: new ol.style.Fill({color: 'rgba(255,255,255,.4)'})
286
            }), image: new ol.style.Icon({
287
                src:         DOKU_BASE + "lib/plugins/openlayersmap/icons/" + p.img,
288
                crossOrigin: '',
289
                opacity:     p.opacity,
290
                scale:       iconScale,
291
                rotation:    p.angle * Math.PI / 180,
292
            }),
293
        }));
294
        vectorSource.addFeature(f);
295
    });
296
297
    overlayGroup.getLayers().push(new ol.layer.Vector({title: 'POI', visible: true, source: vectorSource}));
298
299
    if (mapOpts.kmlfile.length > 0) {
300
        overlayGroup.getLayers().push(new ol.layer.Vector({
301
            title:  'KML file', visible: true,
302
            source: new ol.source.VectorSource({
303
                url:    mapOpts.kmlfile,
304
                format: new ol.format.KML(),
305
            }),
306
            style:  {label: "${name}"}
307
        }));
308
    }
309
310
    if (mapOpts.geojsonfile.length > 0) {
311
        overlayGroup.getLayers().push(new ol.layer.Vector({
312
            title:  'GeoJSON file', visible: true,
313
            source: new ol.source.VectorSource({
314
                url:    mapOpts.geojsonfile,
315
                format: new ol.format.GeoJSON(),
316
            }),
317
            style:  {
318
                /* TODO */
319
                strokeColor:   "#FF00FF",
320
                strokeWidth:   3,
321
                strokeOpacity: 0.7,
322
                pointRadius:   4,
323
                fillColor:     "#FF99FF",
324
                fillOpacity:   0.7
325
            }
326
        }));
327
    }
328
329
    if (mapOpts.gpxfile.length > 0) {
330
        overlayGroup.getLayers().push(new ol.layer.Vector({
331
            title:  'GPS track', visible: true,
332
            source: new ol.source.VectorSource({
333
                url:    mapOpts.gpxfile,
334
                format: new ol.format.GPX(),
335
            }),
336
            style:  {
337
                /* TODO */
338
                strokeColor:   "#0000FF",
339
                strokeWidth:   3,
340
                strokeOpacity: 0.7,
341
                pointRadius:   4,
342
                fillColor:     "#0099FF",
343
                fillOpacity:   0.7
344
            }
345
        }));
346
    }
347
348
    const container = document.getElementById('popup');
349
    const content = document.getElementById('popup-content');
350
    const closer = document.getElementById('popup-closer');
351
352
    const overlay = new ol.Overlay({
353
        element: container, autoPan: true, autoPanAnimation: {
354
            duration: 250,
355
        }, //stopEvent: false,
356
    });
357
    map.addOverlay(overlay);
358
359
    /**
360
     * Add a click handler to hide the popup.
361
     * @return {boolean} Don't follow the href.
362
     */
363
    closer.onclick = function () {
364
        overlay.setPosition(undefined);
365
        closer.blur();
366
        return false;
367
    };
368
369
    // display popup on click
370
    map.on('singleclick', function (evt) {
371
        const selFeature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
372
            return feature;
373
        });
374
        if (selFeature) {
375
            overlay.setPosition(evt.coordinate);
376
377
            let pContent = '<div class="spacer">&nbsp;</div>';
378
            let locDesc = '';
379
380
            if (selFeature.get('rowId') !== undefined) {
381
                pContent += '<span class="rowId">' + selFeature.get('rowId') + ': </span>';
382
            }
383
            if (selFeature.get('name') !== undefined) {
384
                pContent += '<span class="txt">' + selFeature.get('name') + '</span>';
385
                locDesc = selFeature.get('name');
386
                // TODO strip <p> tag from locDesc
387
                // locDesc = selFeature.get('name').split(/\s+/).slice(0,2).join('+');
388
            }
389
            if (selFeature.get('ele') !== undefined) {
390
                pContent += '<div class="ele">elevation: ' + selFeature.get('ele') + '</div>';
391
            }
392
            if (selFeature.get('type') !== undefined) {
393
                pContent += '<div>' + selFeature.get('type') + '</div>';
394
            }
395
            if (selFeature.get('time') !== undefined) {
396
                pContent += '<div class="time">time: ' + selFeature.get('time') + '</div>';
397
            }
398
            if (selFeature.get('description') !== undefined) {
399
                pContent += '<div class="desc">' + selFeature.get('description') + '</div>';
400
            }
401
            if (selFeature.get('img') !== undefined) {
402
                pContent += '<div class="coord" title="lat;lon">' +
403
                    '<img src="' + DOKU_BASE + 'lib/plugins/openlayersmap/icons/' + selFeature.get('img') +
404
                    '" width="16" height="16" ' + 'style="transform:rotate(' + selFeature.get('angle') + 'deg)" />&nbsp;' +
405
                    '<a href="geo:' + selFeature.get('lat') + ',' + selFeature.get('lon') + '?q=' + selFeature.get('lat') +
406
                    ',' + selFeature.get('lon') + '(' + selFeature.get('alt') + ')" title="Open in navigation app">' +
407
                    ol.coordinate.format([selFeature.get('lon'), selFeature.get('lat')], '{x}º; {y}º', 4) + '</a></div>';
0 ignored issues
show
The variable ol seems to be never declared. If this is a global, consider adding a /** global: ol */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
408
            }
409
            content.innerHTML = pContent;
410
        } else {
0 ignored issues
show
Comprehensibility Documentation Best Practice introduced by
This code block is empty. Consider removing it or adding a comment to explain.
Loading history...
411
            // do nothing?...
412
        }
413
    });
414
415
    // change mouse cursor when over marker
416
    map.on('pointermove', function (e) {
417
        const pixel = map.getEventPixel(e.originalEvent);
418
        const hit = map.hasFeatureAtPixel(pixel);
419
        map.getTarget().style.cursor = hit ? 'pointer' : '';
420
    });
421
422
    return map;
423
}
424
425
426
/** init. */
427
function olInit() {
428
    if (olEnable) {
0 ignored issues
show
If you intend to check if the variable olEnable is declared in the current environment, consider using typeof olEnable === "undefined" instead. This is safe if the variable is not actually declared.
Loading history...
429
        // info window
430
        var frag = document.createDocumentFragment(),
431
            temp = document.createElement('div');
432
        temp.innerHTML = '<div id="popup" class="olPopup"><a href="#" id="popup-closer" class="olPopupCloseBox"></a><div id="popup-content"></div></div>';
433
        while (temp.firstChild) {
434
            frag.appendChild(temp.firstChild);
435
        }
436
        console.debug('adding popup', frag);
437
        const b = document.body.appendChild(frag);
0 ignored issues
show
The constant b seems to be never used. Consider removing it.
Loading history...
438
439
        let _i = 0;
440
        // create the maps in the page
441
        for (_i = 0; _i < olMapData.length; _i++) {
0 ignored issues
show
The local (let) variable olMapData is used before it is defined. This will cause a reference error.
Loading history...
442
            var _id = olMapData[_i].mapOpts.id;
443
            console.debug('creating map for', olMapData[_i].mapOpts);
444
            olMaps[_id] = createMap(olMapData[_i].mapOpts, olMapData[_i].poi);
0 ignored issues
show
The local (let) variable olMaps is used before it is defined. This will cause a reference error.
Loading history...
445
446
            // set max-width on help pop-over
447
            jQuery('#' + _id).parent().parent().find('.olMapHelp').css('max-width', olMapData[_i].mapOpts.width);
448
449
            // shrink the map width to fit inside page container
450
            var _w = jQuery('#' + _id + '-olContainer').parent().innerWidth();
451
            if (parseInt(olMapData[_i].mapOpts.width) > _w) {
452
                jQuery('#' + _id).width(_w);
453
                jQuery('#' + _id + '-olStatusBar').width(_w);
454
                jQuery('#' + _id).parent().parent().find('.olMapHelp').width(_w);
455
                olMaps[_id].updateSize();
456
            }
457
        }
458
        // TODO
459
        // let resizeTimer;
460
        // jQuery(window).on('resize', function (e) {
461
        //     clearTimeout(resizeTimer);
462
        //     resizeTimer = setTimeout(function () {
463
        //         for (_i = 0; _i < olMapData.length; _i++) {
464
        //             var _id = olMapData[_i].mapOpts.id;
465
        //             var _w = jQuery('#' + _id + '-olContainer').parent().innerWidth();
466
        //             if (parseInt(olMapData[_i].mapOpts.width) > _w) {
467
        //                 jQuery('#' + _id).width(_w);
468
        //                 jQuery('#' + _id + '-olStatusBar').width(_w);
469
        //                 jQuery('#' + _id).parent().parent().find('.olMapHelp').width(_w);
470
        //                 olMaps[_id].updateSize();
471
        //             }
472
        //         }
473
        //     }, 250);
474
        // });
475
476
        // hide the table(s) with POI by giving it a print-only style
477
        jQuery('.olPOItableSpan').addClass('olPrintOnly');
478
        // hide the static map image(s) by giving it a print only style
479
        jQuery('.olStaticMap').addClass('olPrintOnly');
480
        // add help button with toggle.
481
        jQuery('.olWebOnly > .olMap')
482
            .prepend(
483
                '<div class="olMapHelpButtonDiv">'
484
                + '<button onclick="jQuery(\'.olMapHelp\').toggle(500);" class="olMapHelpButton olHasTooltip"><span>'
485
                + 'Show or hide help</span>?</button></div>');
486
        // toggle to switch dynamic vs. static map
487
        jQuery('.olMapHelp').before(
488
            '<div class="a11y"><button onclick="jQuery(\'.olPrintOnly\').toggle();jQuery(\'.olWebOnly\').toggle();">'
489
            + 'Hide or show the dynamic map</button></div>');
490
    }
491
}
492
493
494
/**
495
 * ol api flag.
496
 *
497
 * @type {Boolean}
498
 */
499
let olEnable = false;
0 ignored issues
show
The variable olEnable seems to be never used. Consider removing it.
Loading history...
500
/**
501
 * An array with data for each map in the page.
502
 *
503
 * @type {Array}
504
 */
505
const olMapData = [];
0 ignored issues
show
The constant olMapData seems to be never used. Consider removing it.
Loading history...
506
/**
507
 * Holds a reference to all of the maps on this page with the map's id as key.
508
 * Can be used as an extension point.
509
 *
510
 * @type {Object}
511
 */
512
let olMaps = {};
0 ignored issues
show
The variable olMaps seems to be never used. Consider removing it.
Loading history...
513
/**
514
 * Stamen tiles flag.
515
 *
516
 * @type {Boolean}
517
 */
518
let stamenEnable = false;
0 ignored issues
show
The variable stamenEnable seems to be never used. Consider removing it.
Loading history...
519
520
/**
521
 * Bing tiles flag.
522
 *
523
 * @type {Boolean}
524
 */
525
let bEnable = false;
0 ignored issues
show
The variable bEnable seems to be never used. Consider removing it.
Loading history...
526
/**
527
 * Bing API key.
528
 *
529
 * @type {String}
530
 */
531
let bApiKey = '';
0 ignored issues
show
The variable bApiKey seems to be never used. Consider removing it.
Loading history...
532
533
/**
534
 * Thunderforest API key.
535
 *
536
 * @type {String}
537
 */
538
let tfApiKey = ''
0 ignored issues
show
The variable tfApiKey seems to be never used. Consider removing it.
Loading history...
539
/**
540
 * OSM tiles flag.
541
 *
542
 * @type {Boolean}
543
 */
544
let osmEnable = true;
0 ignored issues
show
The variable osmEnable seems to be never used. Consider removing it.
Loading history...
545
/**
546
 * CSS support flag.
547
 *
548
 * @type {Boolean}
549
 */
550
let olCSSEnable = true;
0 ignored issues
show
The variable olCSSEnable seems to be never used. Consider removing it.
Loading history...
551
552
/* register olInit to run with onload event. */
553
jQuery(olInit);
554