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