1/* 2 * Copyright (c) 2008-2014 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 * @fileoverview Javascript voor OpenLayers plugin. 19 * 20 * @requires {lib/OpenLayers.js} or a full openlayers build 21 * @author Mark C. Prins <mprins@users.sf.net> 22 * 23 */ 24 25/** 26 * Openlayers selectcontrol. 27 * 28 * @type {OpenLayers.Control.SelectFeature} 29 * @private 30 */ 31var selectControl; 32 33/** 34 * handle feature select event. 35 * 36 * @param {OpenLayers.Feature.Vector} 37 * selFeature the selected feature 38 */ 39function onFeatureSelect(selFeature) { 40 // 'this' is selectFeature control 41 var pPos = selFeature.geometry.getBounds().getCenterLonLat(); 42 // != OpenLayers.Geometry.Point 43 if (selFeature.geometry.CLASS_NAME === "OpenLayers.Geometry.LineString") { 44 try { 45 // for lines make the popup show at the cursor position 46 // TODO this will fail for keyboard select 47 pPos = selFeature.layer.map.getLonLatFromViewPortPx(this.handlers.feature.evt.xy); 48 } catch (anErr) { 49 OpenLayers.Console.warn("unable to get event position; reverting to boundingbox center."); 50 pPos = selFeature.geometry.getBounds().getCenterLonLat(); 51 } 52 } 53 54 var pContent = '<div class="spacer"> </div>'; 55 if (selFeature.data.rowId !== undefined) { 56 pContent += '<span class="rowId">' + selFeature.data.rowId + ': </span>'; 57 } 58 if (selFeature.data.name !== undefined) { 59 pContent += '<span class="txt">' + selFeature.data.name + '</span>'; 60 } 61 if (selFeature.data.ele !== undefined) { 62 pContent += '<div class="ele">elevation: ' + selFeature.data.ele + '</div>'; 63 } 64 if (selFeature.data.type !== undefined) { 65 pContent += '<div>' + selFeature.data.type + '</div>'; 66 } 67 if (selFeature.data.time !== undefined) { 68 pContent += '<div class="time">time: ' + selFeature.data.time + '</div>'; 69 } 70 if (selFeature.data.description !== undefined) { 71 pContent += '<div class="desc">' + selFeature.data.description + '</div>'; 72 } 73 74 if (pContent.length > 0) { 75 // only show when there is something to show... 76 var popup = new OpenLayersMap.Popup.FramedCloud("olPopup", pPos, null, pContent, null, true, function() { 77 selectControl.unselect(selFeature); 78 jQuery('#' + selectControl.layer.map.div.id).focus(); 79 }); 80 selFeature.popup = popup; 81 selFeature.layer.map.addPopup(popup); 82 jQuery('#olPopup').attr("tabindex", -1).focus(); 83 } 84} 85 86/** 87 * handle feature unselect event. remove & destroy the popup. 88 * 89 * @param {OpenLayers.Feature.Vector} 90 * selFeature the un-selected feature 91 */ 92function onFeatureUnselect(selFeature) { 93 if (selFeature.popup !== null) { 94 selFeature.layer.map.removePopup(selFeature.popup); 95 selFeature.popup.destroy(); 96 selFeature.popup = null; 97 } 98} 99/** 100 * Test for css support in the browser by sniffing for a css class we added 101 * using javascript added by the action plugin; this is an edge case because 102 * browsers that support javascript generally support css as well. 103 * 104 * @returns {Boolean} true when the browser supports css (and implicitly 105 * javascript) 106 */ 107function olTestCSSsupport() { 108 return (jQuery('.olCSSsupported').length > 0); 109} 110 111/** 112 * Creates a DocumentFragment to insert into the dom. 113 * 114 * @param mapid 115 * id for the map div 116 * @param width 117 * width for the map div 118 * @param height 119 * height for the map div 120 * @returns a {DocumentFragment} element that can be injected into the dom 121 */ 122function olCreateMaptag(mapid, width, height) { 123 var mEl = '<div id="' + mapid + '-olContainer" class="olContainer olWebOnly">' 124 // map 125 + '<div id="' + mapid + '" tabindex="0" style="width:' + width + ';height:' + height + ';" class="olMap"></div>' 126 // statusbar 127 + '<div id="' + mapid + '-olStatusBar" style="width:' + width + ';"class="olStatusBarContainer">' 128 + ' <div id="' + mapid + '-statusbar-scale" class="olStatusBar olStatusBarScale">scale</div>' 129 + ' <div id="' + mapid + '-statusbar-mouseposition" class="olStatusBar olStatusBarMouseposition"></div>' 130 + ' <div id="' + mapid + '-statusbar-projection" class="olStatusBar olStatusBarProjection">proj</div>' 131 + ' <div id="' + mapid + '-statusbar-text" class="olStatusBar olStatusBarText">txt</div>' 132 + '</div>' 133 // 134 + '</div>', 135 // fragment 136 frag = document.createDocumentFragment(), 137 // temp node 138 temp = document.createElement('div'); 139 temp.innerHTML = mEl; 140 while (temp.firstChild) { 141 frag.appendChild(temp.firstChild); 142 } 143 return frag; 144} 145 146/** 147 * Create the map based on the params given. 148 * 149 * @param {Object} 150 * mapOpts MapOptions hash {id:'olmap', width:500px, height:500px, 151 * lat:6710200, lon:506500, zoom:13, statusbar:1, controls:1, 152 * poihoverstyle:1, baselyr:'', kmlfile:'', gpxfile:'', geojsonfile, 153 * summary:''} 154 * @param {Array} 155 * OLmapPOI array with POI's [ {lat:6710300,lon:506000,txt:'instap 156 * punt',angle:180,opacity:.9,img:'', rowId:n},... ]); 157 * 158 * @return {OpenLayers.Map} the created map 159 */ 160function createMap(mapOpts, OLmapPOI) { 161 if (!olEnable) { 162 return; 163 } 164 if (!olTestCSSsupport()) { 165 olEnable = false; 166 return; 167 } 168 OpenLayers.ImgPath = DOKU_BASE + 'lib/plugins/openlayersmap/lib/img/'; 169 OpenLayers.IMAGE_RELOAD_ATTEMPTS = 3; 170 171 // find map element location 172 var cleartag = document.getElementById(mapOpts.id + '-clearer'); 173 if (cleartag === null) { 174 return; 175 } 176 // create map element and add to document 177 var fragment = olCreateMaptag(mapOpts.id, mapOpts.width, mapOpts.height); 178 cleartag.parentNode.insertBefore(fragment, cleartag); 179 180 /** dynamic map extent. */ 181 var extent = new OpenLayers.Bounds(), 182 183 /** map. */ 184 m = new OpenLayers.Map({ 185 div : mapOpts.id, 186 projection : 'EPSG:900913', 187 displayProjection : new OpenLayers.Projection("EPSG:4326"), 188 numZoomLevels : 22, 189 controls : [], 190 theme : null 191 }); 192 193 if (osmEnable) { 194 /* add OSM map layers */ 195 m.addLayer(new OpenLayers.Layer.OSM()); 196 197 m.addLayer(new OpenLayersMap.Layer.OCM()); 198 /* open cycle map */ 199 m.addLayer(new OpenLayersMap.Layer.OCM("transport", [ 200 "http://a.tile2.opencyclemap.org/transport/${z}/${x}/${y}.png", 201 "http://b.tile2.opencyclemap.org/transport/${z}/${x}/${y}.png", 202 "http://c.tile2.opencyclemap.org/transport/${z}/${x}/${y}.png" ], { 203 visibility : mapOpts.baselyr === "transport" 204 })); 205 m.addLayer(new OpenLayersMap.Layer.OCM("landscape", [ 206 "http://a.tile3.opencyclemap.org/landscape/${z}/${x}/${y}.png", 207 "http://b.tile3.opencyclemap.org/landscape/${z}/${x}/${y}.png", 208 "http://c.tile3.opencyclemap.org/landscape/${z}/${x}/${y}.png" ], { 209 visibility : mapOpts.baselyr === "landscape" 210 })); 211 212 m.addLayer(new OpenLayers.Layer.OSM( 213 "hike and bike map", "http://toolserver.org/tiles/hikebike/${z}/${x}/${y}.png", { 214 visibility : mapOpts.baselyr === "hike and bike map", 215 tileOptions : { 216 crossOriginKeyword : null 217 } 218 })); 219 } 220 /* 221 * add MapQuest map layers, see: 222 * http://developer.mapquest.com/web/products/open/map 223 */ 224 if (mqEnable) { 225 m.addLayer(new OpenLayersMap.Layer.MapQuest()); 226 m.addLayer(new OpenLayersMap.Layer.MapQuest("mapquest sat", [ 227 "//otile1-s.mqcdn.com/tiles/1.0.0/sat/${z}/${x}/${y}.jpg", 228 "//otile2-s.mqcdn.com/tiles/1.0.0/sat/${z}/${x}/${y}.jpg", 229 "//otile3-s.mqcdn.com/tiles/1.0.0/sat/${z}/${x}/${y}.jpg", 230 "//otile4-s.mqcdn.com/tiles/1.0.0/sat/${z}/${x}/${y}.jpg" ], { 231 // note that global coverage is provided at zoom levels 0-11. Zoom 232 // Levels 12+ are provided only in the United States (lower 48). 233 numZoomLevels : 12, 234 visibility : mapOpts.baselyr === "mapquest sat" 235 })); 236 237 } 238 239 if (gEnable) { 240 /* load google maps */ 241 try { 242 m.addLayer(new OpenLayers.Layer.Google("google relief", { 243 type : google.maps.MapTypeId.TERRAIN, 244 numZoomLevels : 16, 245 animationEnabled : true, 246 visibility : mapOpts.baselyr === "google relief" 247 })); 248 m.addLayer(new OpenLayers.Layer.Google("google sat", { 249 type : google.maps.MapTypeId.SATELLITE, 250 animationEnabled : true, 251 visibility : mapOpts.baselyr === "google sat" 252 })); 253 m.addLayer(new OpenLayers.Layer.Google("google hybrid", { 254 type : google.maps.MapTypeId.HYBRID, 255 animationEnabled : true, 256 visibility : mapOpts.baselyr === "google hybrid" 257 })); 258 m.addLayer(new OpenLayers.Layer.Google("google road", { 259 animationEnabled : true, 260 visibility : mapOpts.baselyr === "google road" 261 })); 262 } catch (ol_err1) { 263 Openlayers.Console.userError('Error loading Google maps' + ol_err1); 264 } 265 } 266 267 if (bEnable && bApiKey !== '') { 268 try { 269 /* add Bing tiles */ 270 m.addLayer(new OpenLayers.Layer.Bing({ 271 key : bApiKey, 272 type : "Road", 273 name : "bing road", 274 visibility : mapOpts.baselyr === "bing road", 275 wrapDateLine : true, 276 attributionTemplate : '<a target="_blank" href="http://www.bing.com/maps/">' 277 + 'Bing™</a><img src="http://www.bing.com/favicon.ico" alt="Bing logo"/> ${copyrights}' 278 + '<a target="_blank" href="http://www.microsoft.com/maps/product/terms.html">Terms of Use</a>' 279 })); 280 m.addLayer(new OpenLayers.Layer.Bing({ 281 key : bApiKey, 282 type : "Aerial", 283 name : "bing sat", 284 visibility : mapOpts.baselyr === "bing sat", 285 wrapDateLine : true, 286 attributionTemplate : '<a target="_blank" href="http://www.bing.com/maps/">' 287 + 'Bing™</a><img src="http://www.bing.com/favicon.ico" alt="Bing logo"/> ${copyrights}' 288 + '<a target="_blank" href="http://www.microsoft.com/maps/product/terms.html">Terms of Use</a>' 289 })); 290 m.addLayer(new OpenLayers.Layer.Bing({ 291 key : bApiKey, 292 type : "AerialWithLabels", 293 name : "bing hybrid", 294 visibility : mapOpts.baselyr === "bing hybrid", 295 wrapDateLine : true, 296 attributionTemplate : '<a target="_blank" href="http://www.bing.com/maps/">' 297 + 'Bing™</a><img src="http://www.bing.com/favicon.ico" alt="Bing logo"/> ${copyrights}' 298 + '<a target="_blank" href="http://www.microsoft.com/maps/product/terms.html">Terms of Use</a>' 299 })); 300 } catch (ol_errBing) { 301 Openlayers.Console.userError('Error loading Bing maps: ' + ol_errBing); 302 } 303 } 304 305 m.setCenter(new OpenLayers.LonLat(mapOpts.lon, mapOpts.lat).transform(m.displayProjection, m.projection), 306 mapOpts.zoom); 307 extent.extend(m.getExtent()); 308 309 // change/set alternative baselyr 310 try { 311 m.setBaseLayer(((m.getLayersByName(mapOpts.baselyr))[0])); 312 } catch (ol_err4) { 313 m.setBaseLayer(m.layers[0]); 314 } 315 316 m.addControls([ new OpenLayers.Control.ScaleLine({ 317 geodesic : true 318 }), new OpenLayers.Control.KeyboardDefaults({ 319 observeElement : mapOpts.id 320 }), new OpenLayers.Control.Navigation() ]); 321 322 if (mapOpts.statusbar === 1) { 323 // statusbar control: mouse pos. 324 m.addControl(new OpenLayers.Control.MousePosition({ 325 'div' : OpenLayers.Util.getElement(mapOpts.id + '-statusbar-mouseposition') 326 })); 327 // statusbar control: scale 328 m.addControl(new OpenLayers.Control.Scale(mapOpts.id + '-statusbar-scale')); 329 // statusbar control: attribution 330 m.addControl(new OpenLayers.Control.Attribution({ 331 'div' : OpenLayers.Util.getElement(mapOpts.id + '-statusbar-text') 332 })); 333 // statusbar control: projection 334 OpenLayers.Util.getElement(mapOpts.id + '-statusbar-projection').innerHTML = m.displayProjection; 335 } else { 336 OpenLayers.Util.getElement(mapOpts.id + '-olStatusBar').display = 'none'; 337 } 338 339 if (OLmapPOI.length > 0) { 340 var markers = new OpenLayers.Layer.Vector("POI", { 341 styleMap : new OpenLayers.StyleMap({ 342 "default" : { 343 cursor : "help", 344 externalGraphic : "${img}", 345 graphicHeight : 16, 346 graphicWidth : 16, 347 // graphicXOffset : 0, 348 // graphicYOffset : -8, 349 graphicOpacity : "${opacity}", 350 rotation : "${angle}", 351 backgroundGraphic : DOKU_BASE + "lib/plugins/openlayersmap/icons/marker_shadow.png", 352 // backgroundXOffset : 0, 353 // backgroundYOffset : -4, 354 backgroundRotation : "${angle}", 355 pointRadius : 10, 356 labelXOffset : 8, 357 labelYOffset : 8, 358 labelAlign : "lb", 359 label : "${label}", 360 // fontColor : "", 361 fontFamily : "monospace", 362 fontSize : "12px", 363 fontWeight : "bold" 364 }, 365 "select" : { 366 cursor : "help", 367 externalGraphic : DOKU_BASE + "lib/plugins/openlayersmap/icons/marker-red.png", 368 graphicHeight : 16, 369 graphicWidth : 16, 370 // graphicXOffset : 0, 371 // graphicYOffset : -8, 372 graphicOpacity : 1.0, 373 rotation : "${angle}" 374 } 375 }), 376 isBaseLayer : false, 377 rendererOptions : { 378 yOrdering : true 379 } 380 }); 381 m.addLayer(markers); 382 var features = []; 383 for (var j = 0; j < OLmapPOI.length; j++) { 384 var feat = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(OLmapPOI[j].lon, OLmapPOI[j].lat) 385 .transform(m.displayProjection, m.projection), { 386 angle : OLmapPOI[j].angle, 387 opacity : OLmapPOI[j].opacity, 388 img : DOKU_BASE + "lib/plugins/openlayersmap/icons/" + OLmapPOI[j].img, 389 label : OLmapPOI[j].rowId 390 }); 391 feat.data = { 392 name : OLmapPOI[j].txt, 393 rowId : OLmapPOI[j].rowId 394 }; 395 features.push(feat); 396 } 397 markers.addFeatures(features); 398 extent.extend(markers.getDataExtent()); 399 m.zoomToExtent(extent); 400 } 401 402 /* GPX layer */ 403 if (mapOpts.gpxfile.length > 0) { 404 var layerGPX = new OpenLayers.Layer.Vector("GPS route", { 405 protocol : new OpenLayers.Protocol.HTTP({ 406 url : DOKU_BASE + "lib/exe/fetch.php?media=" + mapOpts.gpxfile, 407 format : new OpenLayers.Format.GPX({ 408 extractWaypoints : true, 409 extractTracks : true, 410 extractStyles : true, 411 extractAttributes : true, 412 handleHeight : true, 413 maxDepth : 3 414 }) 415 }), 416 style : { 417 strokeColor : "#0000FF", 418 strokeWidth : 3, 419 strokeOpacity : 0.7, 420 pointRadius : 4, 421 fillColor : "#0099FF", 422 fillOpacity : 0.7 423 // , label:"${name}" 424 }, 425 projection : new OpenLayers.Projection("EPSG:4326"), 426 strategies : [ new OpenLayers.Strategy.Fixed() ] 427 }); 428 m.addLayer(layerGPX); 429 layerGPX.events.register('loadend', m, function() { 430 extent.extend(layerGPX.getDataExtent()); 431 m.zoomToExtent(extent); 432 }); 433 } 434 435 /* GeoJSON layer */ 436 if (mapOpts.geojsonfile.length > 0) { 437 var layerGJS = new OpenLayers.Layer.Vector("json data", { 438 protocol : new OpenLayers.Protocol.HTTP({ 439 url : DOKU_BASE + "lib/exe/fetch.php?media=" + mapOpts.geojsonfile, 440 format : new OpenLayers.Format.GeoJSON({ 441 ignoreExtraDims : true 442 }) 443 }), 444 style : { 445 strokeColor : "#FF00FF", 446 strokeWidth : 3, 447 strokeOpacity : 0.7, 448 pointRadius : 4, 449 fillColor : "#FF99FF", 450 fillOpacity : 0.7 451 // , label:"${name}" 452 }, 453 projection : new OpenLayers.Projection("EPSG:4326"), 454 strategies : [ new OpenLayers.Strategy.Fixed() ] 455 }); 456 m.addLayer(layerGJS); 457 layerGJS.events.register('loadend', m, function() { 458 extent.extend(layerGJS.getDataExtent()); 459 m.zoomToExtent(extent); 460 }); 461 } 462 463 /* KML layer */ 464 if (mapOpts.kmlfile.length > 0) { 465 var layerKML = new OpenLayers.Layer.Vector("KML file", { 466 protocol : new OpenLayers.Protocol.HTTP({ 467 url : DOKU_BASE + "lib/exe/fetch.php?media=" + mapOpts.kmlfile, 468 format : new OpenLayers.Format.KML({ 469 extractStyles : true, 470 extractAttributes : true, 471 maxDepth : 3 472 }) 473 }), 474 style : { 475 label : "${name}" 476 }, 477 projection : new OpenLayers.Projection("EPSG:4326"), 478 strategies : [ new OpenLayers.Strategy.Fixed() ] 479 }); 480 m.addLayer(layerKML); 481 layerKML.events.register('loadend', m, function() { 482 extent.extend(layerKML.getDataExtent()); 483 m.zoomToExtent(extent); 484 }); 485 } 486 487 // selectcontrol for layers 488 if ((m.getLayersByClass('OpenLayers.Layer.GML').length > 0) 489 || m.getLayersByClass('OpenLayers.Layer.Vector').length > 0) { 490 selectControl = new OpenLayers.Control.SelectFeature((m.getLayersByClass('OpenLayers.Layer.Vector')).concat(m 491 .getLayersByClass('OpenLayers.Layer.GML')), { 492 hover : mapOpts.poihoverstyle, 493 onSelect : onFeatureSelect, 494 onUnselect : onFeatureUnselect 495 }); 496 m.addControl(selectControl); 497 selectControl.activate(); 498 499 // keyboard select control 500 var iControl = new OpenLayersMap.Control.KeyboardClick({ 501 observeElement : mapOpts.id, 502 selectControl : selectControl 503 }); 504 m.addControl(iControl); 505 } 506 507 if (mapOpts.controls === 1) { 508 /* add base controls to map */ 509 m.addControls([ new OpenLayersMap.Control.LayerSwitcher(), new OpenLayers.Control.Graticule({ 510 visible : false 511 }), new OpenLayersMap.Control.OverviewMap({ 512 mapOptions : { 513 theme : null 514 } 515 }), new OpenLayersMap.Control.Zoom() ]); 516 517 // add hillshade, since this is off by default only add when we have a 518 // layerswitcher 519 m.addLayer(new OpenLayers.Layer.OSM("Hillshade", "http://toolserver.org/~cmarqu/hill/${z}/${x}/${y}.png", { 520 isBaseLayer : false, 521 transparent : true, 522 visibility : false, 523 displayOutsideMaxExtent : true, 524 attribution : '', 525 tileOptions : { 526 crossOriginKeyword : null 527 } 528 })); 529 } 530 531 return m; 532} 533 534/** init. */ 535function olInit() { 536 if (olEnable) { 537 var _i = 0; 538 // create the maps in the page 539 for (_i = 0; _i < olMapData.length; _i++) { 540 olMaps[olMapData[_i].mapOpts.id] = createMap(olMapData[_i].mapOpts, olMapData[_i].poi); 541 } 542 543 // hide the table(s) with POI by giving it a print-only style 544 jQuery('.olPOItableSpan').addClass('olPrintOnly'); 545 // hide the static map image(s) by giving it a print only style 546 jQuery('.olStaticMap').addClass('olPrintOnly'); 547 // add help button with toggle. 548 jQuery('.olWebOnly').prepend( 549 '<div class="olMapHelpButtonDiv">' 550 + '<button onclick="jQuery(\'.olMapHelp\').toggle(500);" class="olMapHelpButton olHasTooltip"><span>' 551 + OpenLayers.i18n("toggle_help") 552 + '</span>?</button></div>'); 553 // toggle to switch dynamic vs. static map 554 jQuery('.olMapHelp').before( 555 '<div class="a11y"><button onclick="jQuery(\'.olPrintOnly\').toggle();jQuery(\'.olWebOnly\').toggle();">' 556 + OpenLayers.i18n("toggle_dynamic_map") + '</button></div>'); 557 } 558} 559 560/** 561 * ol api flag. 562 * 563 * @type {Boolean} 564 */ 565var olEnable = false, 566/** 567 * An array with data for each map in the page. 568 * 569 * @type {Array} 570 */ 571olMapData = [], 572/** 573 * Holds a reference to all of the maps on this page with the map's id as key. 574 * Can be used as an extension point. 575 * 576 * @type {Object} 577 */ 578olMaps = new Object(), 579/** 580 * MapQuest tiles flag. 581 * 582 * @type {Boolean} 583 */ 584mqEnable = false, 585/** 586 * google map api flag. 587 * 588 * @type {Boolean} 589 */ 590gEnable = false, 591/** 592 * Bing tiles flag. 593 * 594 * @type {Boolean} 595 */ 596bEnable = false, 597/** 598 * Bing API key. 599 * 600 * @type {String} 601 */ 602bApiKey = '', 603/** 604 * OSM tiles flag. 605 * 606 * @type {Boolean} 607 */ 608osmEnable = true, 609/** 610 * CSS support flag. 611 * 612 * @type {Boolean} 613 */ 614olCSSEnable = true; 615 616/* register olInit to run with onload event. */ 617jQuery(olInit); 618