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