/* * * Copyright (c) 2011-2012, Pavel Shramov * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ // Adaptation Alexandre GALIBERT : // - Define fill opacity for polygons at 0 for transparent rendering // - Disable creation of icons for KML elements // - Specific beahaviour to add an array of files by their urls. A variable will define // the status of the processing (BUSY or READY) and managed with a counter for the number // of files remaining to parse. Once all files parsed, the map zooms on KML spatial elements const KML_READY = 0; const KML_BUSY = 1; L.KML = L.FeatureGroup.extend({ options: { async: true }, initialize: function(kml, options) { L.Util.setOptions(this, options); this._kml = kml; this._status = KML_READY; this._onWaiting = 0; this._layers = {}; if (kml) { this.addKML(kml, options, this.options.async); } }, loadXML: function(url, cb, options, async) { if (async == undefined) async = this.options.async; if (options == undefined) options = this.options; var req = new window.XMLHttpRequest(); req.open('GET', url, async); try { req.overrideMimeType('text/xml'); // unsupported by IE } catch(e) {} req.onreadystatechange = function() { if (req.readyState != 4) return; if(req.status == 200) cb(req.responseXML, options); }; req.send(null); }, addKMLFiles: function(urlList, options, async) { this._status = KML_BUSY; this._onWaiting = urlList.length; for (var index = 0; index < urlList.length; index ++) { this.addKML(urlList[index], options, async); } }, addKML: function(url, options, async) { var _this = this; var cb = function(gpx, options) { _this._addKML(gpx, options) }; this.loadXML(url, cb, options, async); }, _addKML: function(xml, options) { var layers = L.KML.parseKML(xml); if (!layers || !layers.length) return; for (var i = 0; i < layers.length; i++) { this.fire('addlayer', { layer: layers[i] }); this.addLayer(layers[i]); } this.latLngs = L.KML.getLatLngs(xml); this.fire("loaded"); this._onWaiting --; if (this._onWaiting == 0) { this._status = KML_READY; this._map.fitBounds(this.getBounds()); } }, getStatus: function() { return this._status; }, latLngs: [] }); L.Util.extend(L.KML, { parseKML: function (xml) { var style = this.parseStyle(xml); var el = xml.getElementsByTagName("Folder"); var layers = [], l; for (var i = 0; i < el.length; i++) { if (!this._check_folder(el[i])) { continue; } l = this.parseFolder(el[i], style); if (l) { layers.push(l); } } el = xml.getElementsByTagName('Placemark'); for (var j = 0; j < el.length; j++) { if (!this._check_folder(el[j])) { continue; } l = this.parsePlacemark(el[j], xml, style); if (l) { layers.push(l); } } return layers; }, // Return false if e's first parent Folder is not [folder] // - returns true if no parent Folders _check_folder: function (e, folder) { e = e.parentElement; while (e && e.tagName !== "Folder") { e = e.parentElement; } return !e || e === folder; }, parseStyle: function (xml) { var style = {}; var sl = xml.getElementsByTagName("Style"); //for (var i = 0; i < sl.length; i++) { var attributes = {color: true, width: true, Icon: true, href: true, hotSpot: true}; function _parse(xml) { var options = {}; for (var i = 0; i < xml.childNodes.length; i++) { var e = xml.childNodes[i]; var key = e.tagName; if (!attributes[key]) { continue; } if (key === 'hotSpot') { for (var j = 0; j < e.attributes.length; j++) { options[e.attributes[j].name] = e.attributes[j].nodeValue; } } else { var value = e.childNodes[0].nodeValue; if (key === 'color') { options.opacity = parseInt(value.substring(0, 2), 16) / 255.0; options.color = "#" + value.substring(2, 8); } else if (key === 'width') { options.weight = value; } else if (key === 'Icon') { ioptions = _parse(e); if (ioptions.href) { options.href = ioptions.href; } } else if (key === 'href') { options.href = value; } } } options.fillOpacity = 0; return options; } for (var i = 0; i < sl.length; i++) { var e = sl[i], el; var options = {}, poptions = {}, ioptions = {}; el = e.getElementsByTagName("LineStyle"); if (el && el[0]) { options = _parse(el[0]); } el = e.getElementsByTagName("PolyStyle"); if (el && el[0]) { poptions = _parse(el[0]); } if (poptions.color) { options.fillColor = poptions.color; } if (poptions.opacity) { options.fillOpacity = poptions.opacity; } el = e.getElementsByTagName("IconStyle"); if (el && el[0]) { ioptions = _parse(el[0]); } if (ioptions.href) { // save anchor info until the image is loaded options.icon = new L.KMLIcon({ iconUrl: ioptions.href, shadowUrl: null, iconAnchorRef: {x: ioptions.x, y: ioptions.y}, iconAnchorType: {x: ioptions.xunits, y: ioptions.yunits} }); } style['#' + e.getAttribute('id')] = options; } return style; }, parseFolder: function (xml, style) { var el, layers = [], l; el = xml.getElementsByTagName('Folder'); for (var i = 0; i < el.length; i++) { if (!this._check_folder(el[i], xml)) { continue; } l = this.parseFolder(el[i], style); if (l) { layers.push(l); } } el = xml.getElementsByTagName('Placemark'); for (var j = 0; j < el.length; j++) { if (!this._check_folder(el[j], xml)) { continue; } l = this.parsePlacemark(el[j], xml, style); if (l) { layers.push(l); } } if (!layers.length) { return; } if (layers.length === 1) { return layers[0]; } return new L.FeatureGroup(layers); }, parsePlacemark: function (place, xml, style) { var i, j, el, options = {}; el = place.getElementsByTagName('styleUrl'); for (i = 0; i < el.length; i++) { var url = el[i].childNodes[0].nodeValue; for (var a in style[url]) { // for jshint if (true) { options[a] = style[url][a]; } } } var layers = []; var parse = ['LineString', 'Polygon', 'Point']; for (j in parse) { // for jshint if (true) { var tag = parse[j]; el = place.getElementsByTagName(tag); for (i = 0; i < el.length; i++) { var l = this["parse" + tag](el[i], xml, options); if (l) { layers.push(l); } } } } if (!layers.length) { return; } var layer = layers[0]; if (layers.length > 1) { layer = new L.FeatureGroup(layers); } var name, descr = ""; el = place.getElementsByTagName('name'); if (el.length) { name = el[0].childNodes[0].nodeValue; } el = place.getElementsByTagName('description'); for (i = 0; i < el.length; i++) { for (j = 0; j < el[i].childNodes.length; j++) { descr = descr + el[i].childNodes[j].nodeValue; } } /*if (name) { layer.bindPopup("

" + name + "

" + descr); }*/ return layer; }, parseCoords: function (xml) { var el = xml.getElementsByTagName('coordinates'); return this._read_coords(el[0]); }, parseLineString: function (line, xml, options) { var coords = this.parseCoords(line); if (!coords.length) { return; } return new L.Polyline(coords, options); }, parsePoint: function (line, xml, options) { var el = line.getElementsByTagName('coordinates'); if (!el.length) { return; } var ll = el[0].childNodes[0].nodeValue.split(','); return new L.KMLMarker(new L.LatLng(ll[1], ll[0]), options); }, parsePolygon: function (line, xml, options) { var el, polys = [], inner = [], i, coords; el = line.getElementsByTagName('outerBoundaryIs'); for (i = 0; i < el.length; i++) { coords = this.parseCoords(el[i]); if (coords) { polys.push(coords); } } el = line.getElementsByTagName('innerBoundaryIs'); for (i = 0; i < el.length; i++) { coords = this.parseCoords(el[i]); if (coords) { inner.push(coords); } } if (!polys.length) { return; } if (options.fillColor) { options.fill = true; } if (polys.length === 1) { return new L.Polygon(polys.concat(inner), options); } return new L.MultiPolygon(polys, options); }, getLatLngs: function (xml) { var el = xml.getElementsByTagName('coordinates'); var coords = []; for (var j = 0; j < el.length; j++) { // text might span many childnodes coords = coords.concat(this._read_coords(el[j])); } return coords; }, _read_coords: function (el) { var text = "", coords = [], i; for (i = 0; i < el.childNodes.length; i++) { text = text + el.childNodes[i].nodeValue; } text = text.split(/[\s\n]+/); for (i = 0; i < text.length; i++) { var ll = text[i].split(','); if (ll.length < 2) { continue; } coords.push(new L.LatLng(ll[1], ll[0])); } return coords; } }); L.KMLIcon = L.Icon.extend({ createIcon: function () { var img = this._createIcon('icon'); img.onload = function () { var i = new Image(); i.src = this.src; this.style.width = i.width + 'px'; this.style.height = i.height + 'px'; if (this.anchorType.x === 'UNITS_FRACTION' || this.anchorType.x === 'fraction') { img.style.marginLeft = (-this.anchor.x * i.width) + 'px'; } if (this.anchorType.y === 'UNITS_FRACTION' || this.anchorType.x === 'fraction') { img.style.marginTop = (-(1 - this.anchor.y) * i.height) + 'px'; } this.style.display = ""; }; return img; }, _setIconStyles: function (img, name) { L.Icon.prototype._setIconStyles.apply(this, [img, name]) // save anchor information to the image img.anchor = this.options.iconAnchorRef; img.anchorType = this.options.iconAnchorType; } }); L.KMLMarker = L.Marker.extend({ options: { icon: new L.KMLIcon.Default() } });