4

I have a KML file which I need to import in a map using Leaflet. If I use leaflet-kml I can see all polygons with color set from KML file. But because I need to show only one polygon extracted for this file, in another question, someone suggests to use leaflet-omnivore which allow me to do that. In this way, for some reason, can't read the style in KML and use the blue for all polygons.

This is the map loaded with leaflet-kml with colors set by the KML file:

Map with Leaflet-KML

And the code with leaflet-kml:

    <script type="text/javascript">
            // Make basemap
        const key = 'xxx';
        const map = L.map('map').setView([0, 0], 1);
        const gl = L.maplibreGL({
            style: `https://api.maptiler.com/maps/1cc7120b-9404-449d-8aca-dfcde7b44b89/style.json?key=${key}`
        }).addTo(map);

        map.attributionControl.setPrefix('');

            // Load kml file
            fetch('doc.kml')
                .then(res => res.text())
                .then(kmltext => {
                    // Create new kml overlay
                    const parser = new DOMParser();
                    const kml = parser.parseFromString(kmltext, 'text/xml');
                    const track = new L.KML(kml);
                    map.addLayer(track);

                    // Adjust map to show the kml
                    const bounds = track.getBounds();
                    map.fitBounds(bounds);
                });
    </script>

This is the map loaded with leaflet-omnivore:

Map with Leaflet-Omnivore

And the code with leaflet-omnivore:

    <script type="text/javascript">
            // Make basemap
        const key = 'xxx';
        const map = L.map('map').setView([0, 0], 1);
        const gl = L.maplibreGL({
            style: `https://api.maptiler.com/maps/1cc7120b-9404-449d-8aca-dfcde7b44b89/style.json?key=${key}`
        }).addTo(map);

        map.attributionControl.setPrefix('');

            // Load kml file
            var geoJsonLayer = L.geoJSON(null, {
            // Filter desidered number
            //  filter: function(feature) {
            //    return(feature.properties.name == 'Territorio 050');
            //  },
              onEachFeature: function(feature, layer) {
                if (feature.properties.name) {
                  layer.bindPopup(feature.properties.name);
                }
              }
            });

            var kmlLayer = omnivore.kml('doc.kml', null, geoJsonLayer)
              .on('ready', function() {
                map.fitBounds(kmlLayer.getBounds());
              })
              .addTo(map);
    </script>

The KML file:

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <name>Nuova Mappa Residenziale</name>
    <description/>
    <Style id="poly-006064-1000-77-nodesc-highlight">
      <LineStyle>
        <color>ff646000</color>
        <width>1.5</width>
      </LineStyle>
      <PolyStyle>
        <color>4d646000</color>
        <fill>1</fill>
        <outline>1</outline>
      </PolyStyle>
      <BalloonStyle>
        <text><![CDATA[<h3>$[name]</h3>]]></text>
      </BalloonStyle>
    </Style>
    ...
    <StyleMap id="poly-006064-1000-77-nodesc">
      <Pair>
        <key>normal</key>
        <styleUrl>#poly-006064-1000-77-nodesc-normal</styleUrl>
      </Pair>
      <Pair>
        <key>highlight</key>
        <styleUrl>#poly-006064-1000-77-nodesc-highlight</styleUrl>
      </Pair>
    </StyleMap>
    <Folder>
      <name>Fucecchio Alto 001-014</name>
      <Placemark>
        <name>Territorio 001</name>
        <styleUrl>#poly-880E4F-1000-77-nodesc</styleUrl>
        <Polygon>
          <outerBoundaryIs>
            <LinearRing>
              <tessellate>1</tessellate>
              <coordinates>
                10.80684,43.72934,0
                ...
              </coordinates>
            </LinearRing>
          </outerBoundaryIs>
        </Polygon>
      </Placemark>
      <Placemark>
        <name>Territorio 002</name>
        <styleUrl>#poly-A52714-1000-77-nodesc</styleUrl>
        <Polygon>
          <outerBoundaryIs>
            <LinearRing>
              <tessellate>1</tessellate>
              <coordinates>
                10.806808,43.734096,0
                ...
              </coordinates>
            </LinearRing>
          </outerBoundaryIs>
        </Polygon>
      </Placemark>
    </Folder>
    <Folder>
      <name>Fucecchio Nord 015-025</name>
      <Placemark>
        <name>Territorio 015</name>
        <styleUrl>#poly-FF5252-1000-77-nodesc</styleUrl>
        <Polygon>
          <outerBoundaryIs>
            <LinearRing>
              <tessellate>1</tessellate>
              <coordinates>
                10.801615,43.736216,0
                ...
              </coordinates>
            </LinearRing>
          </outerBoundaryIs>
        </Polygon>
      </Placemark>

Full KML file: https://pastebin.com/LuNLcArN

There is a way to read the color from file and show it like leaflet-kml?

2
  • 1
    Please edit your question and add relevant code of how you use both plugins. Also it would be helpful to get your whole KML file, if you can share it.
    – TomazicM
    Commented Apr 12, 2023 at 17:05
  • @TomazicM Thank you. Sorry if I made some mistakes. This is new for me Commented Apr 12, 2023 at 21:03

1 Answer 1

1

One possible solution to style features created by leaflet-omnivore plugin the same way as they are styled with leaflet-kml plugin is to take style analysis code from leaflet-kml plugin code and then apply styles with GeoJSON layer style option.

The following methods/functions have to be taken from leaflet-kml plugin: parseStyles, parseStyle and parseStyleMap. A piece of code dealing with icons has to be commented out in parseStyle function.

Relevant part of the code could then look something like this:

function parseStyles(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;
}

function parseStyle(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;
}

function parseStyleMap(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];
    }
  }
}

var styles;
var geoJsonLayer = L.geoJSON(null, {
  style: function(feature) {
    return(styles[feature.properties.styleUrl]);
  },
  onEachFeature: function(feature, layer) {
    if (feature.properties.name) {
      layer.bindPopup(feature.properties.name);
    }
  }
});

var kmlFile = 'doc.kml';
fetch(kmlFile)
  .then(res => res.text())
  .then(kmltext => {
      const parser = new DOMParser();
      const kml = parser.parseFromString(kmltext, 'text/xml');
      styles = parseStyles(kml);
      parseStyleMap(kml, styles);
      var kmlLayer = omnivore.kml(kmlFile, null, geoJsonLayer)
        .on('ready', function() {
          map.fitBounds(kmlLayer.getBounds());
        })
        .addTo(map);
  });

As a result features are styled as with leaflet-kml plugin:

enter image description here

Here is working JSFiddle: https://jsfiddle.net/TomazicM/ktb63zew/

2
  • I have tried this code on mine but I can't get colors. Shows the polygon but blue. Can you post the code that work for you? Commented Apr 13, 2023 at 11:03
  • 1
    See edited answer with working JSFiddle.
    – TomazicM
    Commented Apr 13, 2023 at 14:03

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.