Subversion Repositories eFlore/Applications.moissonnage

Rev

Rev 28 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
28 alex 1
/*
2
 *
3
 * Copyright (c) 2011-2012, Pavel Shramov
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without modification, are
7
 * permitted provided that the following conditions are met:
8
 *
9
 *    1. Redistributions of source code must retain the above copyright notice, this list of
10
 *       conditions and the following disclaimer.
11
 *
12
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
13
 *       of conditions and the following disclaimer in the documentation and/or other materials
14
 *       provided with the distribution.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
17
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
23
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
 *
26
 */
27
 
31 alex 28
// Adaptation Alexandre GALIBERT :
29
//   - Define fill opacity for polygons at 0 for transparent rendering
30
//   - Disable creation of icons for KML elements
31
//   - Specific beahaviour to add an array of files by their urls. A variable will define
32
//     the status of the processing (BUSY or READY) and managed with a counter for the number
33
//     of files remaining to parse. Once all files parsed, the map zooms on KML spatial elements
28 alex 34
 
31 alex 35
const KML_READY = 0;
36
const KML_BUSY = 1;
37
 
38
 
28 alex 39
L.KML = L.FeatureGroup.extend({
40
	options: {
41
		async: true
42
	},
43
 
44
	initialize: function(kml, options) {
45
		L.Util.setOptions(this, options);
46
		this._kml = kml;
31 alex 47
		this._status = KML_READY;
48
		this._onWaiting = 0;
28 alex 49
		this._layers = {};
50
 
51
		if (kml) {
52
			this.addKML(kml, options, this.options.async);
53
		}
54
	},
55
 
56
	loadXML: function(url, cb, options, async) {
57
		if (async == undefined) async = this.options.async;
58
		if (options == undefined) options = this.options;
59
 
60
		var req = new window.XMLHttpRequest();
61
		req.open('GET', url, async);
62
		try {
63
			req.overrideMimeType('text/xml'); // unsupported by IE
64
		} catch(e) {}
65
		req.onreadystatechange = function() {
66
			if (req.readyState != 4) return;
67
			if(req.status == 200) cb(req.responseXML, options);
68
		};
69
		req.send(null);
70
	},
71
 
31 alex 72
	addKMLFiles: function(urlList, options, async) {
73
		this._status = KML_BUSY;
74
		this._onWaiting = urlList.length;
75
		for (var index = 0; index < urlList.length; index ++) {
76
			this.addKML(urlList[index], options, async);
77
		}
78
	},
79
 
28 alex 80
	addKML: function(url, options, async) {
81
		var _this = this;
82
		var cb = function(gpx, options) { _this._addKML(gpx, options) };
83
		this.loadXML(url, cb, options, async);
84
	},
85
 
86
	_addKML: function(xml, options) {
87
		var layers = L.KML.parseKML(xml);
88
		if (!layers || !layers.length) return;
89
		for (var i = 0; i < layers.length; i++)
90
		{
91
			this.fire('addlayer', {
92
				layer: layers[i]
93
			});
94
			this.addLayer(layers[i]);
95
		}
96
		this.latLngs = L.KML.getLatLngs(xml);
97
		this.fire("loaded");
31 alex 98
		this._onWaiting --;
99
		if (this._onWaiting == 0) {
100
			this._status = KML_READY;
101
			this._map.fitBounds(this.getBounds());
102
		}
28 alex 103
	},
31 alex 104
 
105
	getStatus: function() {
106
		return this._status;
107
	},
28 alex 108
 
109
	latLngs: []
110
});
111
 
112
L.Util.extend(L.KML, {
113
 
114
	parseKML: function (xml) {
115
		var style = this.parseStyle(xml);
116
		var el = xml.getElementsByTagName("Folder");
117
		var layers = [], l;
118
		for (var i = 0; i < el.length; i++) {
119
			if (!this._check_folder(el[i])) { continue; }
120
			l = this.parseFolder(el[i], style);
121
			if (l) { layers.push(l); }
122
		}
123
		el = xml.getElementsByTagName('Placemark');
124
		for (var j = 0; j < el.length; j++) {
125
			if (!this._check_folder(el[j])) { continue; }
126
			l = this.parsePlacemark(el[j], xml, style);
127
			if (l) { layers.push(l); }
128
		}
129
		return layers;
130
	},
131
 
132
	// Return false if e's first parent Folder is not [folder]
133
	// - returns true if no parent Folders
134
	_check_folder: function (e, folder) {
135
		e = e.parentElement;
136
		while (e && e.tagName !== "Folder")
137
		{
138
			e = e.parentElement;
139
		}
140
		return !e || e === folder;
141
	},
142
 
143
	parseStyle: function (xml) {
144
		var style = {};
145
		var sl = xml.getElementsByTagName("Style");
146
 
147
		//for (var i = 0; i < sl.length; i++) {
148
		var attributes = {color: true, width: true, Icon: true, href: true,
149
						  hotSpot: true};
150
 
151
		function _parse(xml) {
152
			var options = {};
153
			for (var i = 0; i < xml.childNodes.length; i++) {
154
				var e = xml.childNodes[i];
155
				var key = e.tagName;
156
				if (!attributes[key]) { continue; }
157
				if (key === 'hotSpot')
158
				{
159
					for (var j = 0; j < e.attributes.length; j++) {
160
						options[e.attributes[j].name] = e.attributes[j].nodeValue;
161
					}
162
				} else {
163
					var value = e.childNodes[0].nodeValue;
164
					if (key === 'color') {
165
						options.opacity = parseInt(value.substring(0, 2), 16) / 255.0;
166
						options.color = "#" + value.substring(2, 8);
167
					} else if (key === 'width') {
168
						options.weight = value;
31 alex 169
					} /*else if (key === 'Icon') {
28 alex 170
						ioptions = _parse(e);
171
						if (ioptions.href) { options.href = ioptions.href; }
31 alex 172
					}*/ else if (key === 'href') {
28 alex 173
						options.href = value;
174
					}
175
				}
176
			}
31 alex 177
			options.fillOpacity = 0;
28 alex 178
			return options;
179
		}
180
 
181
		for (var i = 0; i < sl.length; i++) {
182
			var e = sl[i], el;
183
			var options = {}, poptions = {}, ioptions = {};
184
			el = e.getElementsByTagName("LineStyle");
185
			if (el && el[0]) { options = _parse(el[0]); }
186
			el = e.getElementsByTagName("PolyStyle");
187
			if (el && el[0]) { poptions = _parse(el[0]); }
188
			if (poptions.color) { options.fillColor = poptions.color; }
189
			if (poptions.opacity) { options.fillOpacity = poptions.opacity; }
190
			el = e.getElementsByTagName("IconStyle");
191
			if (el && el[0]) { ioptions = _parse(el[0]); }
192
			if (ioptions.href) {
193
				// save anchor info until the image is loaded
194
				options.icon = new L.KMLIcon({
195
					iconUrl: ioptions.href,
196
					shadowUrl: null,
197
					iconAnchorRef: {x: ioptions.x, y: ioptions.y},
198
					iconAnchorType:	{x: ioptions.xunits, y: ioptions.yunits}
199
				});
200
			}
201
			style['#' + e.getAttribute('id')] = options;
202
		}
203
		return style;
204
	},
205
 
206
	parseFolder: function (xml, style) {
207
		var el, layers = [], l;
208
		el = xml.getElementsByTagName('Folder');
209
		for (var i = 0; i < el.length; i++) {
210
			if (!this._check_folder(el[i], xml)) { continue; }
211
			l = this.parseFolder(el[i], style);
212
			if (l) { layers.push(l); }
213
		}
214
		el = xml.getElementsByTagName('Placemark');
215
		for (var j = 0; j < el.length; j++) {
216
			if (!this._check_folder(el[j], xml)) { continue; }
217
			l = this.parsePlacemark(el[j], xml, style);
218
			if (l) { layers.push(l); }
219
		}
220
		if (!layers.length) { return; }
221
		if (layers.length === 1) { return layers[0]; }
222
		return new L.FeatureGroup(layers);
223
	},
224
 
225
	parsePlacemark: function (place, xml, style) {
226
		var i, j, el, options = {};
227
		el = place.getElementsByTagName('styleUrl');
228
		for (i = 0; i < el.length; i++) {
229
			var url = el[i].childNodes[0].nodeValue;
230
			for (var a in style[url])
231
			{
232
				// for jshint
233
				if (true)
234
				{
235
					options[a] = style[url][a];
236
				}
237
			}
238
		}
239
		var layers = [];
240
 
241
		var parse = ['LineString', 'Polygon', 'Point'];
242
		for (j in parse) {
243
			// for jshint
244
			if (true)
245
			{
246
				var tag = parse[j];
247
				el = place.getElementsByTagName(tag);
248
				for (i = 0; i < el.length; i++) {
249
					var l = this["parse" + tag](el[i], xml, options);
250
					if (l) { layers.push(l); }
251
				}
252
			}
253
		}
254
 
255
		if (!layers.length) {
256
			return;
257
		}
258
		var layer = layers[0];
259
		if (layers.length > 1) {
260
			layer = new L.FeatureGroup(layers);
261
		}
262
 
263
		var name, descr = "";
264
		el = place.getElementsByTagName('name');
265
		if (el.length) {
266
			name = el[0].childNodes[0].nodeValue;
267
		}
268
		el = place.getElementsByTagName('description');
269
		for (i = 0; i < el.length; i++) {
270
			for (j = 0; j < el[i].childNodes.length; j++) {
271
				descr = descr + el[i].childNodes[j].nodeValue;
272
			}
273
		}
274
 
275
		if (name) {
276
			layer.bindPopup("<h2>" + name + "</h2>" + descr);
277
		}
278
 
279
		return layer;
280
	},
281
 
282
	parseCoords: function (xml) {
283
		var el = xml.getElementsByTagName('coordinates');
284
		return this._read_coords(el[0]);
285
	},
286
 
287
	parseLineString: function (line, xml, options) {
288
		var coords = this.parseCoords(line);
289
		if (!coords.length) { return; }
290
		return new L.Polyline(coords, options);
291
	},
292
 
293
	parsePoint: function (line, xml, options) {
294
		var el = line.getElementsByTagName('coordinates');
295
		if (!el.length) {
296
			return;
297
		}
298
		var ll = el[0].childNodes[0].nodeValue.split(',');
299
		return new L.KMLMarker(new L.LatLng(ll[1], ll[0]), options);
300
	},
301
 
302
	parsePolygon: function (line, xml, options) {
303
		var el, polys = [], inner = [], i, coords;
304
		el = line.getElementsByTagName('outerBoundaryIs');
305
		for (i = 0; i < el.length; i++) {
306
			coords = this.parseCoords(el[i]);
307
			if (coords) {
308
				polys.push(coords);
309
			}
310
		}
311
		el = line.getElementsByTagName('innerBoundaryIs');
312
		for (i = 0; i < el.length; i++) {
313
			coords = this.parseCoords(el[i]);
314
			if (coords) {
315
				inner.push(coords);
316
			}
317
		}
318
		if (!polys.length) {
319
			return;
320
		}
321
		if (options.fillColor) {
322
			options.fill = true;
323
		}
324
		if (polys.length === 1) {
325
			return new L.Polygon(polys.concat(inner), options);
326
		}
327
		return new L.MultiPolygon(polys, options);
328
	},
329
 
330
	getLatLngs: function (xml) {
331
		var el = xml.getElementsByTagName('coordinates');
332
		var coords = [];
333
		for (var j = 0; j < el.length; j++) {
334
			// text might span many childnodes
335
			coords = coords.concat(this._read_coords(el[j]));
336
		}
337
		return coords;
338
	},
339
 
340
	_read_coords: function (el) {
341
		var text = "", coords = [], i;
342
		for (i = 0; i < el.childNodes.length; i++) {
343
			text = text + el.childNodes[i].nodeValue;
344
		}
345
		text = text.split(/[\s\n]+/);
346
		for (i = 0; i < text.length; i++) {
347
			var ll = text[i].split(',');
348
			if (ll.length < 2) {
349
				continue;
350
			}
351
			coords.push(new L.LatLng(ll[1], ll[0]));
352
		}
353
		return coords;
354
	}
355
 
356
});
357
 
358
L.KMLIcon = L.Icon.extend({
359
 
360
	createIcon: function () {
361
		var img = this._createIcon('icon');
362
		img.onload = function () {
363
			var i = new Image();
364
			i.src = this.src;
365
			this.style.width = i.width + 'px';
366
			this.style.height = i.height + 'px';
367
 
368
			if (this.anchorType.x === 'UNITS_FRACTION' || this.anchorType.x === 'fraction') {
369
				img.style.marginLeft = (-this.anchor.x * i.width) + 'px';
370
			}
371
			if (this.anchorType.y === 'UNITS_FRACTION' || this.anchorType.x === 'fraction') {
372
				img.style.marginTop  = (-(1 - this.anchor.y) * i.height) + 'px';
373
			}
374
			this.style.display = "";
375
		};
376
		return img;
377
	},
378
 
379
	_setIconStyles: function (img, name) {
380
		L.Icon.prototype._setIconStyles.apply(this, [img, name])
381
		// save anchor information to the image
382
		img.anchor = this.options.iconAnchorRef;
383
		img.anchorType = this.options.iconAnchorType;
384
	}
385
});
386
 
387
 
388
L.KMLMarker = L.Marker.extend({
389
	options: {
390
		icon: new L.KMLIcon.Default()
391
	}
392
});
393