xref: /plugin/openlayersmap/script.js (revision cb3f00d6e0c4d7e12904b95fa8b0c5ed74b1e856) !
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">&nbsp;</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(&quot;' + mapid
124			+ '&quot;).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