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