Raruto / leaflet-kmz

A KMZ file loader for Leaflet Maps
GNU General Public License v3.0
48 stars 27 forks source link

EPSG: 4326 and Groundoverlays support #9

Closed prasanjitdash closed 4 years ago

prasanjitdash commented 4 years ago

Does it parse and display all elements of the KMZ file?

I tried with one sample file (attached below), and it displays only the associated KML file but ignores the PNG images, logo, color bar, etc. The plugin is useful but it would be nice to have a full parse capability as KMZ files are meant to pack various information.

Example file: (please modify the extension: rename to .kmz). S1A_ESA_2020_04_06_07_14_06_0639472446_168.65E_15.46S_VH_C-7_GFS05CDF_wind.ZIP

Below is the HTML, CSS, JS in index.html file. Your help is much appreciated.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>KMZ display</title>
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.1/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="" />
  <script src="https://unpkg.com/leaflet@1.3.1/dist/leaflet.js" integrity="sha512-/Nsx9X4HebavoBvEBuyp3I7od5tA0UzAxs+j83KgC8PU0kgB4XiK4Lfe4y4cgBtaRJQEIFCW+oC506aPT2L1zw==" crossorigin=""></script>
  <script src="https://unpkg.com/leaflet-kmz@latest/dist/leaflet-kmz.js"></script>
  <style>
    html,
    body,
    #map {
      height: 100%;
      width: 100%;
      padding: 0px;
      margin: 0px;
    }
  </style>
</head>

<body>
  <div id="map"></div>
  <script>
    /////////////////////////////////////////////////////////////////////////////////////////////
    //setting up the map//
    /////////////////////////////////////////////////////////////////////////////////////////////
    var map = L.map('map').setView([-15.5, 168.5], 8);

    const ATTR = '&copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, ' +
      '<a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a> | ' +
      '&copy; <a href="http://cartodb.com/attributions">CartoDB</a>';

    const CDB_URL = 'http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png';

    L.tileLayer(CDB_URL, {
      attribution: ATTR
    }).addTo(map);

    var kmzUrl = {your path for KMZ file} + 'S1A_ESA_2020_04_06_07_14_06_0639472446_168.65E_15.46S_VH_C-7_GFS05CDF_wind.kmz'

    // Instantiate KMZ parser (async)
    var kmzParser = new L.KMZParser({
      onKMZLoaded: function(layer, name) {
        layer.addTo(map);
      }
    });
    kmzParser.load(kmzUrl);
  </script>
</body>
</html>
Raruto commented 4 years ago

Hi Prasanjit,

this library depends on tmcw/togeojson for the parsing of kml files, here you can see the currently supported features (i.e. GroundOverlays are not in that list).

As for images, try removing leading / trailing spaces.

For example:

<href> images/modelimage.kml.png </href>

should become:

<href>images/modelimage.kml.png</href>

(however I don't think it will have any effect because this tag is within a <GroundOverlay>)

As a general rule, always try to avoid all those empty spaces within kml tags.

Regards, Raruto

prasanjitdash commented 4 years ago

Thank You, Raruto, for the deeper insight. Much appreciated. I will try as you suggested but got two more associated questions (for just the KML display part):


  1. Is the display independent of CRS? When I tried the prevalent EPSG3857, the location is correct. However, when I made the same display on EPSG4326, it seems displaced.

    EPSG:3857 (WGS 84 / Pseudo-Mercator, units = meters)

    epsg3857

    EPSG: 4326 (WGS 84 / geographic, units = degrees)

    epsg4326


  1. How can I access the Layer created by onKMZLoaded: ? Instead of using Leaflet controls, I have external controls to delete the layer when a user wants to remove the layer.

    Basically, would need access to layer.

    // Instantiate KMZ parser (async)
    var kmzParser = new L.KMZParser({
        onKMZLoaded: function(layer, name) {
          layer.addTo(map);
        }
    });
    kmzParser.load(kmzUrl);

Thanks and regards and let's stay safe wherever we all are. Prasanjit

Raruto commented 4 years ago
  1. Is the display independent of CRS? When I tried the prevalent EPSG3857, the location is correct. However, when I made the same display on EPSG4326, it seems displaced.

Yes, in general leaflet map does not automatically perform conversions for you. Therefore solutions to this can then be many and varied (eg. server / client side reprojection).

As for those two CRS mentioned above, leaflet should be able to natively support them both (but not "simultaneously"), otherwise try to take a look at this library: Proj4Leaflet.

If in doubt, try searching on: gis.stackexchange.com and stackoverflow.com


  1. How can I access the Layer created by onKMZLoaded: ? Instead of using Leaflet controls, I have external controls to delete the layer when a user wants to remove the layer.

In javascript, unpacking zipped files is an asynchronous operation, and therefore, the easiest way to access it is within the callback function:

// Your callback function
function addLayer(layer, name) {
    layer.addTo(map); // <-- same as kmzParser.get(0).layer.addTo(map)
}

// Instantiate KMZ parser (async)
var kmzParser = new L.KMZParser({ onKMZLoaded: addLayer });
kmzParser.load(kmzUrl);

Otherwise you can also try doing such a thing:

<label>
  <input type="checkbox" onchange="toggleLayer(this)" checked>
   toggleLayer
</label>

<script>
  function toggleLayer(e) {
    var kmz = kmzParser.get(0);
    if(kmz) { // <-- check needed because `kmzParser.load(kmzUrl);` is async
      kmz.layer[e.checked ? 'removeFrom' : 'addTo' ](map);
    }  
  }
</script>
prasanjitdash commented 4 years ago
  1. I have Proj4LL in the package and mostly, as you mentioned, leaflet takes care of this natively at least between CRSs: EPSG 3413. 4326. 3031 when I overlay various geoJSON vector layers. Its a bit strange that my current setup is failing between EPSG 3857 and EPSG 4326.

    Oddly enough, I unpacked KMZ on the fly usingJSZip() and did some manual parsing instead of the plugin:

    var parser = new DOMParser();
    var xmlDoc = parser.parseFromString(text, "text/xml");
    var fileData = toGeoJSON.kml(xmlDoc);

    Then just display.

    And the location seems to be fine again. I need to dig deeper and see why 3857/4326 conversion is not working properly natively. But thank you for the tip and I will try to follow a proactive approach, as you suggested.


  1. The callback approach is brilliant. Thank you.

Closing the thread for now.

Raruto commented 4 years ago

I have Proj4LL in the package and mostly, as you mentioned, leaflet takes care of this natively at least between CRSs: EPSG 3413. 4326. 3031 when I overlay various geoJSON vector layers. Its a bit strange that my current setup is failing between EPSG 3857 and EPSG 4326.

Probably the problem is due to the https://github.com/mapbox/geojson-vt/issues/57 library (used within this library to optimize map rendering for large volumes of vector data).

Oddly enough, I unpacked KMZ on the fly using JSZip() and did some manual parsing instead of the plugin

By chance, does it works in this way?

// Instantiate KMZ parser (async)
var kmzParser = new L.KMZParser({
    onKMZLoaded: function(layer, name) {
      layer.addTo(map);
    }
});
kmzParser.load(kmzUrl, { tiled: false } ); // <-- add tiled option here