2
\$\begingroup\$

I have a solution for making the .kml file transparent in the Leaflet Map. The working code looks like this:

L.KML = L.FeatureGroup.extend({

initialize: function (kml) {
    this._kml = kml;
    this._layers = {};

    if (kml) {
        this.addKML(kml);
    }
},

addKML: function (xml) {
    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');
},

latLngs: []
});

 L.Util.extend(L.KML, {

parseKML: function (xml) {
    var style = this.parseStyles(xml);
    this.parseStyleMap(xml, style);
    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); }
    }
    el = xml.getElementsByTagName('GroundOverlay');
    for (var k = 0; k < el.length; k++) {
        l = this.parseGroundOverlay(el[k]);
        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.parentNode;
    while (e && e.tagName !== 'Folder')
    {
        e = e.parentNode;
    }
    return !e || e === folder;
},

parseStyles: function (xml) {
    var styles = {};
    var sl = xml.getElementsByTagName('Style');
    for (var i=0, len=sl.length; i<len; i++) {
        var style = this.parseStyle(sl[i]);
        if (style) {
            var styleName = '#' + style.id;
            styles[styleName] = style;
        }
    }
    return styles;
},

parseStyle: function (xml) {
    var style = {}, poptions = {}, ioptions = {}, el, id;

    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(6, 8) + 
   value.substring(4, 6) + value.substring(2, 4);
                } 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;
                }
            }
        }
        return options;
    }

    el = xml.getElementsByTagName('LineStyle');
    if (el && el[0]) { style = _parse(el[0]); }
    el = xml.getElementsByTagName('PolyStyle');
    if (el && el[0]) { poptions = _parse(el[0]); }
    if (poptions.color) { style.fillColor = poptions.color; }
    if (poptions.opacity) { style.fillOpacity = poptions.opacity; }
    el = xml.getElementsByTagName('IconStyle');
    if (el && el[0]) { ioptions = _parse(el[0]); }
    if (ioptions.href) {
        style.icon = new L.KMLIcon({
            iconUrl: ioptions.href,
            shadowUrl: null,
            anchorRef: {x: ioptions.x, y: ioptions.y},
            anchorType: {x: ioptions.xunits, y: ioptions.yunits}
        });
    }

    id = xml.getAttribute('id');
    if (id && style) {
        style.id = id;
    }

    return style;
   },

    parseStyleMap: function (xml, existingStyles) {
    var sl = xml.getElementsByTagName('StyleMap');

    for (var i = 0; i < sl.length; i++) {
        var e = sl[i], el;
        var smKey, smStyleUrl;

        el = e.getElementsByTagName('key');
        if (el && el[0]) { smKey = el[0].textContent; }
        el = e.getElementsByTagName('styleUrl');
        if (el && el[0]) { smStyleUrl = el[0].textContent; }

        if (smKey === 'normal')
        {
            existingStyles['#' + e.getAttribute('id')] = 
 existingStyles[smStyleUrl];
        }
    }

    return;
},

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); }
    }
    el = xml.getElementsByTagName('GroundOverlay');
    for (var k = 0; k < el.length; k++) {
        if (!this._check_folder(el[k], xml)) { continue; }
        l = this.parseGroundOverlay(el[k]);
        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, options) {
    var h, i, j, k, el, il, opts = 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]) {
            opts[a] = style[url][a];
        }
    }

    il = place.getElementsByTagName('Style')[0];
    if (il) {
        var inlineStyle = this.parseStyle(place);
        if (inlineStyle) {
            for (k in inlineStyle) {
                opts[k] = inlineStyle[k];
            }
        }
    }

    var multi = ['MultiGeometry', 'MultiTrack', 'gx:MultiTrack'];
    for (h in multi) {
        el = place.getElementsByTagName(multi[h]);
        for (i = 0; i < el.length; i++) {
            return this.parsePlacemark(el[i], xml, style, opts);
        }
    }

    var layers = [];

    var parse = ['LineString', 'Polygon', 'Point', 'Track', 'gx:Track'];
    for (j in parse) {
        var tag = parse[j];
        el = place.getElementsByTagName(tag);
        for (i = 0; i < el.length; i++) {
            var l = this['parse' + tag.replace(/gx:/, '')](el[i], xml, opts);
            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 && el[0].childNodes.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.on('add', function () {
            layer.bindPopup('<h2>' + name + '</h2>' + descr, { className: 
  'kml-popup'});
        });
    }

    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);
},

parseTrack: function (line, xml, options) {
    var el = xml.getElementsByTagName('gx:coord');
    if (el.length === 0) { el = xml.getElementsByTagName('coord'); }
    var coords = [];
    for (var j = 0; j < el.length; j++) {
        coords = coords.concat(this._read_gxcoords(el[j]));
    }
    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;
},

_read_gxcoords: function (el) {
    var text = '', coords = [];
    text = el.firstChild.nodeValue.split(' ');
    coords.push(new L.LatLng(text[1], text[0]));
    return coords;
},

parseGroundOverlay: function (xml) {
    var latlonbox = xml.getElementsByTagName('LatLonBox')[0];
    var bounds = new L.LatLngBounds(
        [
            latlonbox.getElementsByTagName('south')[0].childNodes[0].nodeValue,
            latlonbox.getElementsByTagName('west')[0].childNodes[0].nodeValue
        ],
        [
            latlonbox.getElementsByTagName('north')[0].childNodes[0].nodeValue,
            latlonbox.getElementsByTagName('east')[0].childNodes[0].nodeValue
        ]
    );
    var attributes = {Icon: true, href: true, color: true};
    function _parse (xml) {
        var options = {}, ioptions = {};
        for (var i = 0; i < xml.childNodes.length; i++) {
            var e = xml.childNodes[i];
            var key = e.tagName;
            if (!attributes[key]) { continue; }
            var value = e.childNodes[0].nodeValue;
            if (key === 'Icon') {
                ioptions = _parse(e);
                if (ioptions.href) { options.href = ioptions.href; }
            } else if (key === 'href') {
                options.href = value;
            } else if (key === 'color') {
                options.opacity = parseInt(value.substring(0, 2), 16) / 255.0;
                options.color = '#' + value.substring(6, 8) + value.substring(4, 6) + value.substring(2, 4);
            }
        }
        return options;
    }
    var options = {};
    options = _parse(xml);
    if (latlonbox.getElementsByTagName('rotation')[0] !== undefined) {
        var rotation = latlonbox.getElementsByTagName('rotation')[0].childNodes[0].nodeValue;
        options.rotation = parseFloat(rotation);
    }
    return new L.RotatedImageOverlay(options.href, bounds, {opacity: options.opacity, angle: options.rotation});
}

 });

 L.KMLIcon = L.Icon.extend({
options: {
    iconSize: [32, 32],
    iconAnchor: [16, 16],
},
_setIconStyles: function (img, name) {
    L.Icon.prototype._setIconStyles.apply(this, [img, name]);
    if( img.complete ) {
        this.applyCustomStyles( img )
    } else {
        img.onload = this.applyCustomStyles.bind(this,img)
    }

},
applyCustomStyles: function(img) {
    var options = this.options;
    this.options.popupAnchor = [0,(-0.83*img.height)];
    if (options.anchorType.x === 'fraction')
        img.style.marginLeft = (-options.anchorRef.x * img.width) + 'px';
    if (options.anchorType.y === 'fraction')
        img.style.marginTop  = ((-(1 - options.anchorRef.y) * img.height) + 1) + 'px';
    if (options.anchorType.x === 'pixels')
        img.style.marginLeft = (-options.anchorRef.x) + 'px';
    if (options.anchorType.y === 'pixels')
        img.style.marginTop  = (options.anchorRef.y - img.height + 1) + 'px';
}
 });


  L.KMLMarker = L.Marker.extend({
options: {
    icon: new L.KMLIcon.Default()
}
  });

    // Inspired by 
        https://github.com/bbecquet/Leaflet.PolylineDecorator/tree/master/src
      L.RotatedImageOverlay = L.ImageOverlay.extend({
     options: {
    angle: 0
    },
    _reset: function () {
    L.ImageOverlay.prototype._reset.call(this);
    this._rotate();
    },
    _animateZoom: function (e) {
    L.ImageOverlay.prototype._animateZoom.call(this, e);
    this._rotate();
    },
    _rotate: function () {
    if (L.DomUtil.TRANSFORM) {
        // use the CSS transform rule if available
        this._image.style[L.DomUtil.TRANSFORM] += ' rotate(' + 
    this.options.angle + 'deg)';
        } else if (L.Browser.ie) {
        // fallback for IE6, IE7, IE8
        var rad = this.options.angle * (Math.PI / 180),
            costheta = Math.cos(rad),
            sintheta = Math.sin(rad);
        this._image.style.filter += ' 
    progid:DXImageTransform.Microsoft.Matrix(sizingMethod=\'auto expand\', M11=' +
            costheta + ', M12=' + (-sintheta) + ', M21=' + sintheta + ', M22=' + costheta + ')';
    }
},
getBounds: function () {
    return this._bounds;
      }
   });
      // ---------- end L.KML -----------------------------

           var myKML = '<?xml version="1.0" encoding="UTF-8"?>' +
        '<kml xmlns="http://www.opengis.net/kml/2.2" 
     xmlns:gx="http://www.google.com/kml/ext/2.2" 
   xmlns:kml="http://www.opengis.net/kml/2.2" 
   xmlns:atom="http://www.w3.org/2005/Atom">' +
  '<Document>' +
  '<GroundOverlay>' +
'<name>Coventry.tif</name>' +
'<description>http://localhost/testc/Coventry.tif</description>' +
'<Icon>' +
    '<href>https://i.imgur.com/58CbNhB.png</href>' +
    '<viewBoundScale>0.75</viewBoundScale>' +
'</Icon>' +
'<color>55ffffff</color>' +
'<LatLonBox>' +
    '<north>52.39388512224325</north>' +
    '<south>52.39299906007814</south>' +
    '<east>-1.458241039649089</east>' +
    '<west>-1.460061203494303</west>' +
'</LatLonBox>' +
   '</GroundOverlay>' +
    '<GroundOverlay>' +
     '<name>Coventry2.tif</name>' +
     '<description>http://localhost/testc/Coventry.tif2</description>' +
     '<Icon>' +
    '<href>https://i.imgur.com/58CbNhB.png</href>' +
    '<viewBoundScale>0.75</viewBoundScale>' +
    '<color>00ff6633</color>' +
     '</Icon>' +
      '<LatLonBox>' +
    '<north>52.39299906007814</north>' +
    '<south>52.39199906007814</south>' +
    '<east>-1.458241039649089</east>' +
    '<west>-1.460061203494303</west>' +
    '</LatLonBox>' +
   '</GroundOverlay>' +
  '</Document>' +
  '</kml>'; 


   parser = new DOMParser();
   kml = parser.parseFromString(myKML,"text/xml");
   track = new L.KML(kml);

  map.addLayer(track);
  const bounds = track.getBounds();
  map.fitBounds( bounds );


  sldOpacity.onchange = function() {
  document.getElementById('image-opacity').innerHTML = this.value;
  track.setStyle({fillOpacity: this.value});
 }

Whereas the 1st part code of the L.KML.js, that belongs to the leaflet-kml plugin based here:

https://github.com/windycom/leaflet-kml

and can be attached as an internal script.

then an another section of the code must be inside ther main script. I am not convinced to the form. Here I have to separate each single line of the XML code, punctuating it line be line. Is it possible to make a direct fetch of the .kml file as per the code below:

 var track

var kml = fetch('Coventry.kml')
      .then( res => res.text() )
      .then( kmltext => {

            // Create new kml overlay
            parser = new DOMParser();
            kml = parser.parseFromString(kmltext,"text/xml");
            track = new L.KML(kml);
            //console.log(kml)

            //const track = new L.KML(kml)
            map.addLayer(track)
            // Adjust map to show the kml
            const bounds = track.getBounds()
            map.fitBounds( bounds )
      });

in order to avoid typing each single line of the .kml file?

\$\endgroup\$
1
  • \$\begingroup\$ Here I have to separate each single line of the XML code, punctuating it line be line.” Can you edit your post and better explain what that means? Does it have to do with how myKML is parsed? \$\endgroup\$ Nov 2, 2019 at 5:55

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.