3 L
.KML
= L
.FeatureGroup
.extend({
8 initialize: function(kml
, options
) {
9 L
.Util
.setOptions(this, options
);
14 this.addKML(kml
, options
, this.options
.async
);
18 loadXML: function(url
, cb
, options
, async
) {
19 if (async
== undefined) async
= this.options
.async
;
20 if (options
== undefined) options
= this.options
;
22 var req
= new window
.XMLHttpRequest();
23 req
.open('GET', url
, async
);
25 req
.overrideMimeType('text/xml'); // unsupported by IE
27 req
.onreadystatechange = function() {
28 if (req
.readyState
!= 4) return;
29 if(req
.status
== 200) cb(req
.responseXML
, options
);
34 addKML: function(url
, options
, async
) {
36 var cb = function(gpx
, options
) { _this
._addKML(gpx
, options
) };
37 this.loadXML(url
, cb
, options
, async
);
40 _addKML: function(xml
, options
) {
41 var layers
= L
.KML
.parseKML(xml
);
42 if (!layers
|| !layers
.length
) return;
43 for (var i
= 0; i
< layers
.length
; i
++)
45 this.fire('addlayer', {
48 this.addLayer(layers
[i
]);
50 this.latLngs
= L
.KML
.getLatLngs(xml
);
57 L
.Util
.extend(L
.KML
, {
59 parseKML: function (xml
) {
60 var style
= this.parseStyle(xml
);
61 this.parseStyleMap(xml
, style
);
62 var el
= xml
.getElementsByTagName("Folder");
64 for (var i
= 0; i
< el
.length
; i
++) {
65 if (!this._check_folder(el
[i
])) { continue; }
66 l
= this.parseFolder(el
[i
], style
);
67 if (l
) { layers
.push(l
); }
69 el
= xml
.getElementsByTagName('Placemark');
70 for (var j
= 0; j
< el
.length
; j
++) {
71 if (!this._check_folder(el
[j
])) { continue; }
72 l
= this.parsePlacemark(el
[j
], xml
, style
);
73 if (l
) { layers
.push(l
); }
78 // Return false if e's first parent Folder is not [folder]
79 // - returns true if no parent Folders
80 _check_folder: function (e
, folder
) {
82 while (e
&& e
.tagName
!== "Folder")
86 return !e
|| e
=== folder
;
89 parseStyle: function (xml
) {
91 var sl
= xml
.getElementsByTagName("Style");
93 //for (var i = 0; i < sl.length; i++) {
94 var attributes
= {color
: true, width
: true, Icon
: true, href
: true,
97 function _parse(xml
) {
99 for (var i
= 0; i
< xml
.childNodes
.length
; i
++) {
100 var e
= xml
.childNodes
[i
];
102 if (!attributes
[key
]) { continue; }
103 if (key
=== 'hotSpot')
105 for (var j
= 0; j
< e
.attributes
.length
; j
++) {
106 options
[e
.attributes
[j
].name
] = e
.attributes
[j
].nodeValue
;
109 var value
= e
.childNodes
[0].nodeValue
;
110 if (key
=== 'color') {
111 options
.opacity
= parseInt(value
.substring(0, 2), 16) / 255.0;
112 options
.color
= "#" + value
.substring(6, 8) + value
.substring(4, 6) + value
.substring(2, 4);
113 } else if (key
=== 'width') {
114 options
.weight
= value
;
115 } else if (key
=== 'Icon') {
116 ioptions
= _parse(e
);
117 if (ioptions
.href
) { options
.href
= ioptions
.href
; }
118 } else if (key
=== 'href') {
119 options
.href
= value
;
126 for (var i
= 0; i
< sl
.length
; i
++) {
128 var options
= {}, poptions
= {}, ioptions
= {};
129 el
= e
.getElementsByTagName("LineStyle");
130 if (el
&& el
[0]) { options
= _parse(el
[0]); }
131 el
= e
.getElementsByTagName("PolyStyle");
132 if (el
&& el
[0]) { poptions
= _parse(el
[0]); }
133 if (poptions
.color
) { options
.fillColor
= poptions
.color
; }
134 if (poptions
.opacity
) { options
.fillOpacity
= poptions
.opacity
; }
135 el
= e
.getElementsByTagName("IconStyle");
136 if (el
&& el
[0]) { ioptions
= _parse(el
[0]); }
138 // save anchor info until the image is loaded
139 options
.icon
= new L
.KMLIcon({
140 iconUrl
: ioptions
.href
,
142 iconAnchorRef
: {x
: ioptions
.x
, y
: ioptions
.y
},
143 iconAnchorType
: {x
: ioptions
.xunits
, y
: ioptions
.yunits
}
146 style
['#' + e
.getAttribute('id')] = options
;
151 parseStyleMap: function (xml
, existingStyles
) {
152 var sl
= xml
.getElementsByTagName("StyleMap");
154 for (var i
= 0; i
< sl
.length
; i
++) {
156 var smKey
, smStyleUrl
;
158 el
= e
.getElementsByTagName("key");
159 if (el
&& el
[0]) { smKey
= el
[0].textContent
; }
160 el
= e
.getElementsByTagName("styleUrl");
161 if (el
&& el
[0]) { smStyleUrl
= el
[0].textContent
; }
165 existingStyles
['#' + e
.getAttribute('id')] = existingStyles
[smStyleUrl
];
172 parseFolder: function (xml
, style
) {
173 var el
, layers
= [], l
;
174 el
= xml
.getElementsByTagName('Folder');
175 for (var i
= 0; i
< el
.length
; i
++) {
176 if (!this._check_folder(el
[i
], xml
)) { continue; }
177 l
= this.parseFolder(el
[i
], style
);
178 if (l
) { layers
.push(l
); }
180 el
= xml
.getElementsByTagName('Placemark');
181 for (var j
= 0; j
< el
.length
; j
++) {
182 if (!this._check_folder(el
[j
], xml
)) { continue; }
183 l
= this.parsePlacemark(el
[j
], xml
, style
);
184 if (l
) { layers
.push(l
); }
186 if (!layers
.length
) { return; }
187 if (layers
.length
=== 1) { return layers
[0]; }
188 return new L
.FeatureGroup(layers
);
191 parsePlacemark: function (place
, xml
, style
) {
192 var i
, j
, el
, options
= {};
193 el
= place
.getElementsByTagName('styleUrl');
194 for (i
= 0; i
< el
.length
; i
++) {
195 var url
= el
[i
].childNodes
[0].nodeValue
;
196 for (var a
in style
[url
])
201 options
[a
] = style
[url
][a
];
207 var parse
= ['LineString', 'Polygon', 'Point'];
213 el
= place
.getElementsByTagName(tag
);
214 for (i
= 0; i
< el
.length
; i
++) {
215 var l
= this["parse" + tag
](el
[i
], xml
, options
);
216 if (l
) { layers
.push(l
); }
221 if (!layers
.length
) {
224 var layer
= layers
[0];
225 if (layers
.length
> 1) {
226 layer
= new L
.FeatureGroup(layers
);
229 var name
, descr
= "";
230 el
= place
.getElementsByTagName('name');
231 if (el
.length
&& el
[0].childNodes
.length
) {
232 name
= el
[0].childNodes
[0].nodeValue
;
234 el
= place
.getElementsByTagName('description');
235 for (i
= 0; i
< el
.length
; i
++) {
236 for (j
= 0; j
< el
[i
].childNodes
.length
; j
++) {
237 descr
= descr
+ el
[i
].childNodes
[j
].nodeValue
;
242 layer
.bindPopup("<h2>" + name
+ "</h2>" + descr
);
248 parseCoords: function (xml
) {
249 var el
= xml
.getElementsByTagName('coordinates');
250 return this._read_coords(el
[0]);
253 parseLineString: function (line
, xml
, options
) {
254 var coords
= this.parseCoords(line
);
255 if (!coords
.length
) { return; }
256 return new L
.Polyline(coords
, options
);
259 parsePoint: function (line
, xml
, options
) {
260 var el
= line
.getElementsByTagName('coordinates');
264 var ll
= el
[0].childNodes
[0].nodeValue
.split(',');
265 return new L
.KMLMarker(new L
.LatLng(ll
[1], ll
[0]), options
);
268 parsePolygon: function (line
, xml
, options
) {
269 var el
, polys
= [], inner
= [], i
, coords
;
270 el
= line
.getElementsByTagName('outerBoundaryIs');
271 for (i
= 0; i
< el
.length
; i
++) {
272 coords
= this.parseCoords(el
[i
]);
277 el
= line
.getElementsByTagName('innerBoundaryIs');
278 for (i
= 0; i
< el
.length
; i
++) {
279 coords
= this.parseCoords(el
[i
]);
287 if (options
.fillColor
) {
290 if (polys
.length
=== 1) {
291 return new L
.Polygon(polys
.concat(inner
), options
);
293 return new L
.MultiPolygon(polys
, options
);
296 getLatLngs: function (xml
) {
297 var el
= xml
.getElementsByTagName('coordinates');
299 for (var j
= 0; j
< el
.length
; j
++) {
300 // text might span many childNodes
301 coords
= coords
.concat(this._read_coords(el
[j
]));
306 _read_coords: function (el
) {
307 var text
= "", coords
= [], i
;
308 for (i
= 0; i
< el
.childNodes
.length
; i
++) {
309 text
= text
+ el
.childNodes
[i
].nodeValue
;
311 text
= text
.split(/[\s\n]+/);
312 for (i
= 0; i
< text
.length
; i
++) {
313 var ll
= text
[i
].split(',');
317 coords
.push(new L
.LatLng(ll
[1], ll
[0]));
324 L
.KMLIcon
= L
.Icon
.extend({
326 createIcon: function () {
327 var img
= this._createIcon('icon');
328 img
.onload = function () {
330 this.style
.width
= i
.width
+ 'px';
331 this.style
.height
= i
.height
+ 'px';
333 if (this.anchorType
.x
=== 'UNITS_FRACTION' || this.anchorType
.x
=== 'fraction') {
334 img
.style
.marginLeft
= (-this.anchor
.x
* i
.width
) + 'px';
336 if (this.anchorType
.y
=== 'UNITS_FRACTION' || this.anchorType
.x
=== 'fraction') {
337 img
.style
.marginTop
= (-(1 - this.anchor
.y
) * i
.height
) + 'px';
339 this.style
.display
= "";
344 _setIconStyles: function (img
, name
) {
345 L
.Icon
.prototype._setIconStyles
.apply(this, [img
, name
])
346 // save anchor information to the image
347 img
.anchor
= this.options
.iconAnchorRef
;
348 img
.anchorType
= this.options
.iconAnchorType
;
353 L
.KMLMarker
= L
.Marker
.extend({
355 icon
: new L
.KMLIcon
.Default()