Edit

Custom Drag-and-Drop (KMZ)

drag-and-drop3 kml9 kmz1



 

Example of using the drag-and-drop interaction with a custom format to handle KMZ files.

Example of using the drag-and-drop interaction with a custom format to handle KMZ files. In addition to the formats used in the Drag-and-Drop example a custom format (subclassing KML) is used to handle KMZ files. KML and icons must be extracted from the KMZ array buffer synchronously. JSZip 2.x is used as it has better browser compatibility and is simpler to code than the more recent JSZip-sync. There is no projection transform support, so this will only work with data in EPSG:4326 and EPSG:3857.

main.js
import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import {
  DragAndDrop,
  defaults as defaultInteractions,
} from 'ol/interaction';
import {GPX, GeoJSON, IGC, KML, TopoJSON} from 'ol/format';
import {OSM, Vector as VectorSource} from 'ol/source';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';

// Create functions to extract KML and icons from KMZ array buffer,
// which must be done synchronously.

var zip = new JSZip();

function getKMLData(buffer) {
  var kmlData;
  zip.load(buffer);
  var kmlFile = zip.file(/.kml$/i)[0];
  if (kmlFile) {
    kmlData = kmlFile.asText();
  }
  return kmlData;
}

function getKMLImage(href) {
  var url = href;
  var path = window.location.href;
  path = path.slice(0, path.lastIndexOf('/') + 1);
  if (href.indexOf(path) === 0) {
    var regexp = new RegExp(href.replace(path, '') + '$', 'i');
    var kmlFile = zip.file(regexp)[0];
    if (kmlFile) {
      url = URL.createObjectURL(new Blob([kmlFile.asArrayBuffer()]));
    }
  }
  return url;
}

// Define a KMZ format class by subclassing ol/format/KML

var KMZ = /*@__PURE__*/(function (KML) {
  function KMZ(opt_options) {
    var options = opt_options || {};
    options.iconUrlFunction = getKMLImage;
    KML.call(this, options);
  }

  if ( KML ) KMZ.__proto__ = KML;
  KMZ.prototype = Object.create( KML && KML.prototype );
  KMZ.prototype.constructor = KMZ;

  KMZ.prototype.getType = function getType () {
    return 'arraybuffer';
  };

  KMZ.prototype.readFeature = function readFeature (source, options) {
    var kmlData = getKMLData(source);
    return KML.prototype.readFeature.call(this, kmlData, options);
  };

  KMZ.prototype.readFeatures = function readFeatures (source, options) {
    var kmlData = getKMLData(source);
    return KML.prototype.readFeatures.call(this, kmlData, options);
  };

  return KMZ;
}(KML));

// Set up map with Drag and Drop interaction

var dragAndDropInteraction = new DragAndDrop({
  formatConstructors: [KMZ, GPX, GeoJSON, IGC, KML, TopoJSON],
});

var map = new Map({
  interactions: defaultInteractions().extend([dragAndDropInteraction]),
  layers: [
    new TileLayer({
      source: new OSM(),
    }) ],
  target: 'map',
  view: new View({
    center: [0, 0],
    zoom: 2,
  }),
});

dragAndDropInteraction.on('addfeatures', function (event) {
  var vectorSource = new VectorSource({
    features: event.features,
  });
  map.addLayer(
    new VectorLayer({
      source: vectorSource,
    })
  );
  map.getView().fit(vectorSource.getExtent());
});

var displayFeatureInfo = function (pixel) {
  var features = [];
  map.forEachFeatureAtPixel(pixel, function (feature) {
    features.push(feature);
  });
  if (features.length > 0) {
    var info = [];
    var i, ii;
    for (i = 0, ii = features.length; i < ii; ++i) {
      var description =
        features[i].get('description') ||
        features[i].get('name') ||
        features[i].get('_name') ||
        features[i].get('layer');
      if (description) {
        info.push(description);
      }
    }
    document.getElementById('info').innerHTML = info.join('<br/>') || '&nbsp';
  } else {
    document.getElementById('info').innerHTML = '&nbsp;';
  }
};

map.on('pointermove', function (evt) {
  if (evt.dragging) {
    return;
  }
  var pixel = map.getEventPixel(evt.originalEvent);
  displayFeatureInfo(pixel);
});

map.on('click', function (evt) {
  displayFeatureInfo(evt.pixel);
});

// Sample data download

var link = document.getElementById('download');

function download(fullpath, filename) {
  fetch(fullpath)
    .then(function (response) {
      return response.blob();
    })
    .then(function (blob) {
      if (navigator.msSaveBlob) {
        // link download attribuute does not work on MS browsers
        navigator.msSaveBlob(blob, filename);
      } else {
        link.href = URL.createObjectURL(blob);
        link.download = filename;
        link.click();
      }
    });
}

document.getElementById('download-kmz').addEventListener('click', function () {
  download('data/kmz/iceland.kmz', 'iceland.kmz');
});
index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Custom Drag-and-Drop (KMZ)</title>
    <!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer -->
    <script src="https://unpkg.com/elm-pep"></script>
    <!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
    <script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=fetch,requestAnimationFrame,Element.prototype.classList,URL,TextDecoder"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/2.6.1/jszip.min.js"></script>
    <style>
      .map {
        width: 100%;
        height:400px;
      }
      #info {
        width: 100%;
        height: 24rem;
        overflow: scroll;
        display: flex;
        align-items: baseline;
        border: 1px solid black;
        justify-content: flex-start;
      }
    </style>
  </head>
  <body>
    <div id="map" class="map"></div>
    <br />
    <div>
      <a id="download" download></a>
      <button id="download-kmz">Download sample</button>
    </div>
    <br />
    <div id="info">&nbsp;</div>
    <script src="main.js"></script>
  </body>
</html>
package.json
{
  "name": "drag-and-drop-custom-kmz",
  "dependencies": {
    "ol": "6.5.0"
  },
  "devDependencies": {
    "parcel": "^2.0.0-beta.1"
  },
  "scripts": {
    "start": "parcel index.html",
    "build": "parcel build --public-url . index.html"
  }
}