acdc4e8f3db8c8cb77c3a9e2026f8f5a9328c19e
1 L
.KML
= L
.FeatureGroup
.extend({
6 initialize: function(kml
, options
) {
7 L
.Util
.setOptions(this, options
);
12 this.addKML(kml
, options
, this.options
.async
);
16 loadXML: function(url
, cb
, options
, async
) {
17 if (async
=== undefined) async
= this.options
.async
;
18 if (options
=== undefined) options
= this.options
;
20 var req
= new window
.XMLHttpRequest();
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
);
36 setTimeout(function () { xdr
.send(); }, 0);
38 req
.open('GET', url
, async
);
40 req
.overrideMimeType('text/xml'); // unsupported by IE
42 req
.onreadystatechange = function () {
43 if (req
.readyState
!== 4) return;
44 if (req
.status
=== 200) cb(req
.responseXML
, options
);
50 addKML: function(url
, options
, async
) {
52 var cb = function(gpx
, options
) { _this
._addKML(gpx
, options
); };
53 this.loadXML(url
, cb
, options
, async
);
56 _addKML: function(xml
, options
) {
57 var layers
= L
.KML
.parseKML(xml
);
58 if (!layers
|| !layers
.length
) return;
59 for (var i
= 0; i
< layers
.length
; i
++) {
60 this.fire('addlayer', {
63 this.addLayer(layers
[i
]);
65 this.latLngs
= L
.KML
.getLatLngs(xml
);
72 L
.Util
.extend(L
.KML
, {
74 parseKML: function (xml
) {
75 var style
= this.parseStyles(xml
);
76 this.parseStyleMap(xml
, style
);
77 var el
= xml
.getElementsByTagName('Folder');
79 for (var i
= 0; i
< el
.length
; i
++) {
80 if (!this._check_folder(el
[i
])) { continue; }
81 l
= this.parseFolder(el
[i
], style
);
82 if (l
) { layers
.push(l
); }
84 el
= xml
.getElementsByTagName('Placemark');
85 for (var j
= 0; j
< el
.length
; j
++) {
86 if (!this._check_folder(el
[j
])) { continue; }
87 l
= this.parsePlacemark(el
[j
], xml
, style
);
88 if (l
) { layers
.push(l
); }
90 el
= xml
.getElementsByTagName('GroundOverlay');
91 for (var k
= 0; k
< el
.length
; k
++) {
92 l
= this.parseGroundOverlay(el
[k
]);
93 if (l
) { layers
.push(l
); }
98 // Return false if e's first parent Folder is not [folder]
99 // - returns true if no parent Folders
100 _check_folder: function (e
, folder
) {
102 while (e
&& e
.tagName
!== 'Folder')
106 return !e
|| e
=== folder
;
109 parseStyles: function(xml
) {
111 var sl
= xml
.getElementsByTagName('Style');
112 for (var i
=0, len
=sl
.length
; i
<len
; i
++) {
113 var style
= this.parseStyle(sl
[i
]);
115 var styleName
= '#' + style
.id
;
116 styles
[styleName
] = style
;
122 parseStyle: function (xml
) {
123 var style
= {}, poptions
= {}, ioptions
= {}, el
, id
;
125 var attributes
= { color
: true, width
: true, Icon
: true, href
: true, hotSpot
: true };
127 function _parse(xml
) {
129 for (var i
= 0; i
< xml
.childNodes
.length
; i
++) {
130 var e
= xml
.childNodes
[i
];
132 if (!attributes
[key
]) { continue; }
133 if (key
=== 'hotSpot')
135 for (var j
= 0; j
< e
.attributes
.length
; j
++) {
136 options
[e
.attributes
[j
].name
] = e
.attributes
[j
].nodeValue
;
139 var value
= e
.childNodes
[0].nodeValue
;
140 if (key
=== 'color') {
141 options
.opacity
= parseInt(value
.substring(0, 2), 16) / 255.0;
142 options
.color
= '#' + value
.substring(6, 8) + value
.substring(4, 6) + value
.substring(2, 4);
143 } else if (key
=== 'width') {
144 options
.weight
= value
;
145 } else if (key
=== 'Icon') {
146 ioptions
= _parse(e
);
147 if (ioptions
.href
) { options
.href
= ioptions
.href
; }
148 } else if (key
=== 'href') {
149 options
.href
= value
;
156 el
= xml
.getElementsByTagName('LineStyle');
157 if (el
&& el
[0]) { style
= _parse(el
[0]); }
158 el
= xml
.getElementsByTagName('PolyStyle');
159 if (el
&& el
[0]) { poptions
= _parse(el
[0]); }
160 if (poptions
.color
) { style
.fillColor
= poptions
.color
; }
161 if (poptions
.opacity
) { style
.fillOpacity
= poptions
.opacity
; }
162 el
= xml
.getElementsByTagName('IconStyle');
163 if (el
&& el
[0]) { ioptions
= _parse(el
[0]); }
165 style
.icon
= new L
.KMLIcon({
166 iconUrl
: ioptions
.href
,
168 anchorRef
: {x
: ioptions
.x
, y
: ioptions
.y
},
169 anchorType
: {x
: ioptions
.xunits
, y
: ioptions
.yunits
}
173 id
= xml
.getAttribute('id');
181 parseStyleMap: function (xml
, existingStyles
) {
182 var sl
= xml
.getElementsByTagName('StyleMap');
184 for (var i
= 0; i
< sl
.length
; i
++) {
186 var smKey
, smStyleUrl
;
188 el
= e
.getElementsByTagName('key');
189 if (el
&& el
[0]) { smKey
= el
[0].textContent
; }
190 el
= e
.getElementsByTagName('styleUrl');
191 if (el
&& el
[0]) { smStyleUrl
= el
[0].textContent
; }
193 if (smKey
=== 'normal')
195 existingStyles
['#' + e
.getAttribute('id')] = existingStyles
[smStyleUrl
];
202 parseFolder: function (xml
, style
) {
203 var el
, layers
= [], l
;
204 el
= xml
.getElementsByTagName('Folder');
205 for (var i
= 0; i
< el
.length
; i
++) {
206 if (!this._check_folder(el
[i
], xml
)) { continue; }
207 l
= this.parseFolder(el
[i
], style
);
208 if (l
) { layers
.push(l
); }
210 el
= xml
.getElementsByTagName('Placemark');
211 for (var j
= 0; j
< el
.length
; j
++) {
212 if (!this._check_folder(el
[j
], xml
)) { continue; }
213 l
= this.parsePlacemark(el
[j
], xml
, style
);
214 if (l
) { layers
.push(l
); }
216 el
= xml
.getElementsByTagName('GroundOverlay');
217 for (var k
= 0; k
< el
.length
; k
++) {
218 if (!this._check_folder(el
[k
], xml
)) { continue; }
219 l
= this.parseGroundOverlay(el
[k
]);
220 if (l
) { layers
.push(l
); }
222 if (!layers
.length
) { return; }
223 if (layers
.length
=== 1) { return layers
[0]; }
224 return new L
.FeatureGroup(layers
);
227 parsePlacemark: function (place
, xml
, style
) {
228 var h
, i
, j
, k
, el
, il
, options
= {};
230 var multi
= ['MultiGeometry', 'MultiTrack', 'gx:MultiTrack'];
232 el
= place
.getElementsByTagName(multi
[h
]);
233 for (i
= 0; i
< el
.length
; i
++) {
234 return this.parsePlacemark(el
[i
], xml
, style
);
238 el
= place
.getElementsByTagName('styleUrl');
239 for (i
= 0; i
< el
.length
; i
++) {
240 var url
= el
[i
].childNodes
[0].nodeValue
;
241 for (var a
in style
[url
]) {
242 options
[a
] = style
[url
][a
];
246 il
= place
.getElementsByTagName('Style')[0];
248 var inlineStyle
= this.parseStyle(place
);
250 for (k
in inlineStyle
) {
251 options
[k
] = inlineStyle
[k
];
258 var parse
= ['LineString', 'Polygon', 'Point', 'Track', 'gx:Track'];
261 el
= place
.getElementsByTagName(tag
);
262 for (i
= 0; i
< el
.length
; i
++) {
263 var l
= this['parse' + tag
.replace(/gx:/, '')](el
[i
], xml
, options
);
264 if (l
) { layers
.push(l
); }
268 if (!layers
.length
) {
271 var layer
= layers
[0];
272 if (layers
.length
> 1) {
273 layer
= new L
.FeatureGroup(layers
);
276 var name
, descr
= '';
277 el
= place
.getElementsByTagName('name');
278 if (el
.length
&& el
[0].childNodes
.length
) {
279 name
= el
[0].childNodes
[0].nodeValue
;
281 el
= place
.getElementsByTagName('description');
282 for (i
= 0; i
< el
.length
; i
++) {
283 for (j
= 0; j
< el
[i
].childNodes
.length
; j
++) {
284 descr
= descr
+ el
[i
].childNodes
[j
].nodeValue
;
289 layer
.on('add', function(e
) {
290 layer
.bindPopup('<h2>' + name
+ '</h2>' + descr
);
297 parseCoords: function (xml
) {
298 var el
= xml
.getElementsByTagName('coordinates');
299 return this._read_coords(el
[0]);
302 parseLineString: function (line
, xml
, options
) {
303 var coords
= this.parseCoords(line
);
304 if (!coords
.length
) { return; }
305 return new L
.Polyline(coords
, options
);
308 parseTrack: function (line
, xml
, options
) {
309 var el
= xml
.getElementsByTagName('gx:coord');
310 if (el
.length
=== 0) { el
= xml
.getElementsByTagName('coord'); }
312 for (var j
= 0; j
< el
.length
; j
++) {
313 coords
= coords
.concat(this._read_gxcoords(el
[j
]));
315 if (!coords
.length
) { return; }
316 return new L
.Polyline(coords
, options
);
319 parsePoint: function (line
, xml
, options
) {
320 var el
= line
.getElementsByTagName('coordinates');
324 var ll
= el
[0].childNodes
[0].nodeValue
.split(',');
325 return new L
.KMLMarker(new L
.LatLng(ll
[1], ll
[0]), options
);
328 parsePolygon: function (line
, xml
, options
) {
329 var el
, polys
= [], inner
= [], i
, coords
;
330 el
= line
.getElementsByTagName('outerBoundaryIs');
331 for (i
= 0; i
< el
.length
; i
++) {
332 coords
= this.parseCoords(el
[i
]);
337 el
= line
.getElementsByTagName('innerBoundaryIs');
338 for (i
= 0; i
< el
.length
; i
++) {
339 coords
= this.parseCoords(el
[i
]);
347 if (options
.fillColor
) {
350 if (polys
.length
=== 1) {
351 return new L
.Polygon(polys
.concat(inner
), options
);
353 return new L
.MultiPolygon(polys
, options
);
356 getLatLngs: function (xml
) {
357 var el
= xml
.getElementsByTagName('coordinates');
359 for (var j
= 0; j
< el
.length
; j
++) {
360 // text might span many childNodes
361 coords
= coords
.concat(this._read_coords(el
[j
]));
366 _read_coords: function (el
) {
367 var text
= '', coords
= [], i
;
368 for (i
= 0; i
< el
.childNodes
.length
; i
++) {
369 text
= text
+ el
.childNodes
[i
].nodeValue
;
371 text
= text
.split(/[\s\n]+/);
372 for (i
= 0; i
< text
.length
; i
++) {
373 var ll
= text
[i
].split(',');
377 coords
.push(new L
.LatLng(ll
[1], ll
[0]));
382 _read_gxcoords: function (el
) {
383 var text
= '', coords
= [];
384 text
= el
.firstChild
.nodeValue
.split(' ');
385 coords
.push(new L
.LatLng(text
[1], text
[0]));
389 parseGroundOverlay: function (xml
) {
390 var latlonbox
= xml
.getElementsByTagName('LatLonBox')[0];
391 var bounds
= new L
.LatLngBounds(
393 latlonbox
.getElementsByTagName('south')[0].childNodes
[0].nodeValue
,
394 latlonbox
.getElementsByTagName('west')[0].childNodes
[0].nodeValue
397 latlonbox
.getElementsByTagName('north')[0].childNodes
[0].nodeValue
,
398 latlonbox
.getElementsByTagName('east')[0].childNodes
[0].nodeValue
401 var attributes
= {Icon
: true, href
: true, color
: true};
402 function _parse(xml
) {
403 var options
= {}, ioptions
= {};
404 for (var i
= 0; i
< xml
.childNodes
.length
; i
++) {
405 var e
= xml
.childNodes
[i
];
407 if (!attributes
[key
]) { continue; }
408 var value
= e
.childNodes
[0].nodeValue
;
409 if (key
=== 'Icon') {
410 ioptions
= _parse(e
);
411 if (ioptions
.href
) { options
.href
= ioptions
.href
; }
412 } else if (key
=== 'href') {
413 options
.href
= value
;
414 } else if (key
=== 'color') {
415 options
.opacity
= parseInt(value
.substring(0, 2), 16) / 255.0;
416 options
.color
= '#' + value
.substring(6, 8) + value
.substring(4, 6) + value
.substring(2, 4);
422 options
= _parse(xml
);
423 if (latlonbox
.getElementsByTagName('rotation')[0] !== undefined) {
424 var rotation
= latlonbox
.getElementsByTagName('rotation')[0].childNodes
[0].nodeValue
;
425 options
.rotation
= parseFloat(rotation
);
427 return new L
.RotatedImageOverlay(options
.href
, bounds
, {opacity
: options
.opacity
, angle
: options
.rotation
});
432 L
.KMLIcon
= L
.Icon
.extend({
433 _setIconStyles: function (img
, name
) {
434 L
.Icon
.prototype._setIconStyles
.apply(this, [img
, name
]);
435 var options
= this.options
;
436 this.options
.popupAnchor
= [0,(-0.83*img
.height
)];
437 if (options
.anchorType
.x
=== 'fraction')
438 img
.style
.marginLeft
= (-options
.anchorRef
.x
* img
.width
) + 'px';
439 if (options
.anchorType
.y
=== 'fraction')
440 img
.style
.marginTop
= ((-(1 - options
.anchorRef
.y
) * img
.height
) + 1) + 'px';
441 if (options
.anchorType
.x
=== 'pixels')
442 img
.style
.marginLeft
= (-options
.anchorRef
.x
) + 'px';
443 if (options
.anchorType
.y
=== 'pixels')
444 img
.style
.marginTop
= (options
.anchorRef
.y
- img
.height
+ 1) + 'px';
449 L
.KMLMarker
= L
.Marker
.extend({
451 icon
: new L
.KMLIcon
.Default()
455 // Inspired by https://github.com/bbecquet/Leaflet.PolylineDecorator/tree/master/src
456 L
.RotatedImageOverlay
= L
.ImageOverlay
.extend({
460 _reset: function () {
461 L
.ImageOverlay
.prototype._reset
.call(this);
464 _animateZoom: function (e
) {
465 L
.ImageOverlay
.prototype._animateZoom
.call(this, e
);
468 _rotate: function () {
469 if (L
.DomUtil
.TRANSFORM
) {
470 // use the CSS transform rule if available
471 this._image
.style
[L
.DomUtil
.TRANSFORM
] += ' rotate(' + this.options
.angle
+ 'deg)';
472 } else if(L
.Browser
.ie
) {
473 // fallback for IE6, IE7, IE8
474 var rad
= this.options
.angle
* (Math
.PI
/ 180),
475 costheta
= Math
.cos(rad
),
476 sintheta
= Math
.sin(rad
);
477 this._image
.style
.filter
+= ' progid:DXImageTransform.Microsoft.Matrix(sizingMethod=\'auto expand\', M11=' +
478 costheta
+ ', M12=' + (-sintheta
) + ', M21=' + sintheta
+ ', M22=' + costheta
+ ')';
481 getBounds: function() {