PageSourceSearch

https://mcng.cat/maps/leaflet-plugins/layer/vector/KML.js?v1

js mcng.cat collected 2026-10-02 12:14:07 UTC 13,809 bytes, 486 lines download raw bytes

1L.KML = L.FeatureGroup.extend({
2	options: {
3		async: true
4	},
5
6	initialize: function (kml, options) {
7		L.Util.setOptions(this, options);
8		this._kml = kml;
9		this._layers = {};
10
11		if (kml) {
12			this.addKML(kml, options, this.options.async);
13		}
14	},
15
16	loadXML: function (url, cb, options, async) {
17		if (async === undefined) async = this.options.async;
18		if (options === undefined) options = this.options;
19
20		var req = new window.XMLHttpRequest();
21		
22		// Check for IE8 and IE9 Fix Cors for those browsers
23		if (req.withCredentials === undefined && typeof window.XDomainRequest !== 'undefined') {
24			var xdr = new window.XDomainRequest();
25			xdr.open('GET', url, async);
26			xdr.onprogress = function () { };
27			xdr.ontimeout = function () { };
28			xdr.onerror = function () { };
29			xdr.onload = function () {
30				if (xdr.responseText) {
31					var xml = new window.ActiveXObject('Microsoft.XMLDOM');
32					xml.loadXML(xdr.responseText);
33					cb(xml, options);
34				}
35			};
36			setTimeout(function () { xdr.send(); }, 0);
37		} else {
38			req.open('GET', url, async);
39			req.setRequestHeader('Accept', 'application/vnd.google-earth.kml+xml');
40			try {
41				req.overrideMimeType('text/xml'); // unsupported by IE
42			} catch (e) { }
43			req.onreadystatechange = function () {
44				if (req.readyState !== 4) return;
45				if (req.status === 200) cb(req.responseXML, options);
46			};
47			req.send(null);
48		}
49	},
50
51	addKML: function (url, options, async) {
52		var _this = this;
53		var cb = function (kml) { _this._addKML(kml); };
54		this.loadXML(url, cb, options, async);
55	},
56
57	_addKML: function (xml) {
58		var layers = L.KML.parseKML(xml);
59		if (!layers || !layers.length) return;
60		for (var i = 0; i < layers.length; i++) {
61			this.fire('addlayer', {
62				layer: layers[i]
63			});
64			this.addLayer(layers[i]);
65		}
66		this.latLngs = L.KML.getLatLngs(xml);
67		this.fire('loaded');
68	},
69
70	latLngs: []
71});
72
73L.Util.extend(L.KML, {
74
75	parseKML: function (xml) {
76		var style = this.parseStyles(xml);
77		this.parseStyleMap(xml, style);
78		var el = xml.getElementsByTagName('Folder');
79		var layers = [], l;
80		for (var i = 0; i < el.length; i++) {
81			if (!this._check_folder(el[i])) { continue; }
82			l = this.parseFolder(el[i], style);
83			if (l) { layers.push(l); }
84		}
85		el = xml.getElementsByTagName('Placemark');
86		for (var j = 0; j < el.length; j++) {
87			if (!this._check_folder(el[j])) { continue; }
88			l = this.parsePlacemark(el[j], xml, style);
89			if (l) { layers.push(l); }
90		}
91		el = xml.getElementsByTagName('GroundOverlay');
92		for (var k = 0; k < el.length; k++) {
93			l = this.parseGroundOverlay(el[k]);
94			if (l) { layers.push(l); }
95		}
96		return layers;
97	},
98
99	// Return false if e's first parent Folder is not [folder]
100	// - returns true if no parent Folders
101	_check_folder: function (e, folder) {
102		e = e.parentNode;
103		while (e && e.tagName !== 'Folder')
104		{
105			e = e.parentNode;
106		}
107		return !e || e === folder;
108	},
109
110	parseStyles: function (xml) {
111		var styles = {};
112		var sl = xml.getElementsByTagName('Style');
113		for (var i=0, len=sl.length; i<len; i++) {
114			var style = this.parseStyle(sl[i]);
115			if (style) {
116				var styleName = '#' + style.id;
117				styles[styleName] = style;
118			}
119		}
120		return styles;
121	},
122
123	parseStyle: function (xml) {
124		var style = {}, poptions = {}, ioptions = {}, el, id;
125
126		var attributes = {color: true, width: true, Icon: true, href: true, hotSpot: true};
127
128		function _parse (xml) {
129			var options = {};
130			for (var i = 0; i < xml.childNodes.length; i++) {
131				var e = xml.childNodes[i];
132				var key = e.tagName;
133				if (!attributes[key]) { continue; }
134				if (key === 'hotSpot')
135				{
136					for (var j = 0; j < e.attributes.length; j++) {
137						options[e.attributes[j].name] = e.attributes[j].nodeValue;
138					}
139				} else {
140					var value = e.childNodes[0].nodeValue;
141					if (key === 'color') {
142						options.opacity = parseInt(value.substring(0, 2), 16) / 255.0;
143						options.color = '#' + value.substring(6, 8) + value.substring(4, 6) + value.substring(2, 4);
144					} else if (key === 'width') {
145						options.weight = value;
146					} else if (key === 'Icon') {
147						ioptions = _parse(e);
148						if (ioptions.href) { options.href = ioptions.href; }
149					} else if (key === 'href') {
150						options.href = value;
151					}
152				}
153			}
154			return options;
155		}
156
157		el = xml.getElementsByTagName('LineStyle');
158		if (el && el[0]) { style = _parse(el[0]); }
159		el = xml.getElementsByTagName('PolyStyle');
160		if (el && el[0]) { poptions = _parse(el[0]); }
161		if (poptions.color) { style.fillColor = poptions.color; }
162		if (poptions.opacity) { style.fillOpacity = poptions.opacity; }
163		el = xml.getElementsByTagName('IconStyle');
164		if (el && el[0]) { ioptions = _parse(el[0]); }
165		if (ioptions.href) {
166			style.icon = new L.KMLIcon({
167				iconUrl: ioptions.href,
168				shadowUrl: null,
169				anchorRef: {x: ioptions.x, y: ioptions.y},
170				anchorType:	{x: ioptions.xunits, y: ioptions.yunits}
171			});
172		}
173		
174		id = xml.getAttribute('id');
175		if (id && style) {
176			style.id = id;
177		}
178		
179		return style;
180	},
181	
182	parseStyleMap: function (xml, existingStyles) {
183		var sl = xml.getElementsByTagName('StyleMap');
184		
185		for (var i = 0; i < sl.length; i++) {
186			var e = sl[i], el;
187			var smKey, smStyleUrl;
188			
189			el = e.getElementsByTagName('key');
190			if (el && el[0]) { smKey = el[0].textContent; }
191			el = e.getElementsByTagName('styleUrl');
192			if (el && el[0]) { smStyleUrl = el[0].textContent; }
193			
194			if (smKey === 'normal')
195			{
196				existingStyles['#' + e.getAttribute('id')] = existingStyles[smStyleUrl];
197			}
198		}
199		
200		return;
201	},
202
203	parseFolder: function (xml, style) {
204		var el, layers = [], l;
205		el = xml.getElementsByTagName('Folder');
206		for (var i = 0; i < el.length; i++) {
207			if (!this._check_folder(el[i], xml)) { continue; }
208			l = this.parseFolder(el[i], style);
209			if (l) { layers.push(l); }
210		}
211		el = xml.getElementsByTagName('Placemark');
212		for (var j = 0; j < el.length; j++) {
213			if (!this._check_folder(el[j], xml)) { continue; }
214			l = this.parsePlacemark(el[j], xml, style);
215			if (l) { layers.push(l); }
216		}
217		el = xml.getElementsByTagName('GroundOverlay');
218		for (var k = 0; k < el.length; k++) {
219			if (!this._check_folder(el[k], xml)) { continue; }
220			l = this.parseGroundOverlay(el[k]);
221			if (l) { layers.push(l); }
222		}
223		if (!layers.length) { return; }
224		if (layers.length === 1) { return layers[0]; }
225		return new L.FeatureGroup(layers);
226	},
227
228	parsePlacemark: function (place, xml, style, options) {
229		var h, i, j, k, el, il, opts = options || {};
230
231		el = place.getElementsByTagName('styleUrl');
232		for (i = 0; i < el.length; i++) {
233			var url = el[i].childNodes[0].nodeValue;
234			for (var a in style[url]) {
235				opts[a] = style[url][a];
236			}
237		}
238		
239		il = place.getElementsByTagName('Style')[0];
240		if (il) {
241			var inlineStyle = this.parseStyle(place);
242			if (inlineStyle) {
243				for (k in inlineStyle) {
244					opts[k] = inlineStyle[k];
245				}
246			}
247		}
248
249		var multi = ['MultiGeometry', 'MultiTrack', 'gx:MultiTrack'];
250		for (h in multi) {
251			el = place.getElementsByTagName(multi[h]);
252			for (i = 0; i < el.length; i++) {
253				return this.parsePlacemark(el[i], xml, style, opts);
254			}
255		}
256		
257		var layers = [];
258
259		var parse = ['LineString', 'Polygon', 'Point', 'Track', 'gx:Track'];
260		for (j in parse) {
261			var tag = parse[j];
262			el = place.getElementsByTagName(tag);
263			for (i = 0; i < el.length; i++) {
264				var l = this['parse' + tag.replace(/gx:/, '')](el[i], xml, opts);
265				if (l) { layers.push(l); }
266			}
267		}
268
269		if (!layers.length) {
270			return;
271		}
272		var layer = layers[0];
273		if (layers.length > 1) {
274			layer = new L.FeatureGroup(layers);
275		}
276
277		var name, descr = '';
278		el = place.getElementsByTagName('name');
279		if (el.length && el[0].childNodes.length) {
280			name = el[0].childNodes[0].nodeValue;
281		}
282		el = place.getElementsByTagName('description');
283		for (i = 0; i < el.length; i++) {
284			for (j = 0; j < el[i].childNodes.length; j++) {
285				descr = descr + el[i].childNodes[j].nodeValue;
286			}
287		}
288
289		if (name) {
290			layer.on('add', function () {
291				layer.bindPopup('<h2>' + name + '</h2>' + descr);
292			});
293		}
294
295		return layer;
296	},
297
298	parseCoords: function (xml) {
299		var el = xml.getElementsByTagName('coordinates');
300		return this._read_coords(el[0]);
301	},
302
303	parseLineString: function (line, xml, options) {
304		var coords = this.parseCoords(line);
305		if (!coords.length) { return; }
306		return new L.Polyline(coords, options);
307	},
308
309	parseTrack: function (line, xml, options) {
310		var el = xml.getElementsByTagName('gx:coord');
311		if (el.length === 0) { el = xml.getElementsByTagName('coord'); }
312		var coords = [];
313		for (var j = 0; j < el.length; j++) {
314			coords = coords.concat(this._read_gxcoords(el[j]));
315		}
316		if (!coords.length) { return; }
317		return new L.Polyline(coords, options);
318	},
319
320	parsePoint: function (line, xml, options) {
321		var el = line.getElementsByTagName('coordinates');
322		if (!el.length) {
323			return;
324		}
325		var ll = el[0].childNodes[0].nodeValue.split(',');
326		return new L.KMLMarker(new L.LatLng(ll[1], ll[0]), options);
327	},
328
329	parsePolygon: function (line, xml, options) {
330		var el, polys = [], inner = [], i, coords;
331		el = line.getElementsByTagName('outerBoundaryIs');
332		for (i = 0; i < el.length; i++) {
333			coords = this.parseCoords(el[i]);
334			if (coords) {
335				polys.push(coords);
336			}
337		}
338		el = line.getElementsByTagName('innerBoundaryIs');
339		for (i = 0; i < el.length; i++) {
340			coords = this.parseCoords(el[i]);
341			if (coords) {
342				inner.push(coords);
343			}
344		}
345		if (!polys.length) {
346			return;
347		}
348		if (options.fillColor) {
349			options.fill = true;
350		}
351		if (polys.length === 1) {
352			return new L.Polygon(polys.concat(inner), options);
353		}
354		return new L.MultiPolygon(polys, options);
355	},
356
357	getLatLngs: function (xml) {
358		var el = xml.getElementsByTagName('coordinates');
359		var coords = [];
360		for (var j = 0; j < el.length; j++) {
361			// text might span many childNodes
362			coords = coords.concat(this._read_coords(el[j]));
363		}
364		return coords;
365	},
366
367	_read_coords: function (el) {
368		var text = '', coords = [], i;
369		for (i = 0; i < el.childNodes.length; i++) {
370			text = text + el.childNodes[i].nodeValue;
371		}
372		text = text.split(/[\s\n]+/);
373		for (i = 0; i < text.length; i++) {
374			var ll = text[i].split(',');
375			if (ll.length < 2) {
376				continue;
377			}
378			coords.push(new L.LatLng(ll[1], ll[0]));
379		}
380		return coords;
381	},
382
383	_read_gxcoords: function (el) {
384		var text = '', coords = [];
385		text = el.firstChild.nodeValue.split(' ');
386		coords.push(new L.LatLng(text[1], text[0]));
387		return coords;
388	},
389
390	parseGroundOverlay: function (xml) {
391		var latlonbox = xml.getElementsByTagName('LatLonBox')[0];
392		var bounds = new L.LatLngBounds(
393			[
394				latlonbox.getElementsByTagName('south')[0].childNodes[0].nodeValue,
395				latlonbox.getElementsByTagName('west')[0].childNodes[0].nodeValue
396			],
397			[
398				latlonbox.getElementsByTagName('north')[0].childNodes[0].nodeValue,
399				latlonbox.getElementsByTagName('east')[0].childNodes[0].nodeValue
400			]
401		);
402		var attributes = {Icon: true, href: true, color: true};
403		function _parse (xml) {
404			var options = {}, ioptions = {};
405			for (var i = 0; i < xml.childNodes.length; i++) {
406				var e = xml.childNodes[i];
407				var key = e.tagName;
408				if (!attributes[key]) { continue; }
409				var value = e.childNodes[0].nodeValue;
410				if (key === 'Icon') {
411					ioptions = _parse(e);
412					if (ioptions.href) { options.href = ioptions.href; }
413				} else if (key === 'href') {
414					options.href = value;
415				} else if (key === 'color') {
416					options.opacity = parseInt(value.substring(0, 2), 16) / 255.0;
417					options.color = '#' + value.substring(6, 8) + value.substring(4, 6) + value.substring(2, 4);
418				}
419			}
420			return options;
421		}
422		var options = {};
423		options = _parse(xml);
424		if (latlonbox.getElementsByTagName('rotation')[0] !== undefined) {
425			var rotation = latlonbox.getElementsByTagName('rotation')[0].childNodes[0].nodeValue;
426			options.rotation = parseFloat(rotation);
427		}
428		return new L.RotatedImageOverlay(options.href, bounds, {opacity: options.opacity, angle: options.rotation});
429	}
430
431});
432
433L.KMLIcon = L.Icon.extend({
434	_setIconStyles: function (img, name) {
435		L.Icon.prototype._setIconStyles.apply(this, [img, name]);
436		var options = this.options;
437		this.options.popupAnchor = [0,(-0.83*img.height)];
438		if (options.anchorType.x === 'fraction')
439			img.style.marginLeft = (-options.anchorRef.x * img.width) + 'px';
440		if (options.anchorType.y === 'fraction')
441			img.style.marginTop  = ((-(1 - options.anchorRef.y) * img.height) + 1) + 'px';
442		if (options.anchorType.x === 'pixels')
443			img.style.marginLeft = (-options.anchorRef.x) + 'px';
444		if (options.anchorType.y === 'pixels')
445			img.style.marginTop  = (options.anchorRef.y - img.height + 1) + 'px';
446	}
447});
448
449
450L.KMLMarker = L.Marker.extend({
451	options: {
452		icon: new L.KMLIcon.Default()
453	}
454});
455
456// Inspired by https://github.com/bbecquet/Leaflet.PolylineDecorator/tree/master/src
457L.RotatedImageOverlay = L.ImageOverlay.extend({
458	options: {
459		angle: 0
460	},
461	_reset: function () {
462		L.ImageOverlay.prototype._reset.call(this);
463		this._rotate();
464	},
465	_animateZoom: function (e) {
466		L.ImageOverlay.prototype._animateZoom.call(this, e);
467		this._rotate();
468	},
469	_rotate: function () {
470        if (L.DomUtil.TRANSFORM) {
471            // use the CSS transform rule if available
472            this._image.style[L.DomUtil.TRANSFORM] += ' rotate(' + this.options.angle + 'deg)';
473        } else if (L.Browser.ie) {
474            // fallback for IE6, IE7, IE8
475            var rad = this.options.angle * (Math.PI / 180),
476                costheta = Math.cos(rad),
477                sintheta = Math.sin(rad);
478            this._image.style.filter += ' progid:DXImageTransform.Microsoft.Matrix(sizingMethod=\'auto expand\', M11=' + 
479                costheta + ', M12=' + (-sintheta) + ', M21=' + sintheta + ', M22=' + costheta + ')';                
480        }
481	},
482	getBounds: function () {
483		return this._bounds;
484	}
485});
486

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.