(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = global || self, factory(global['leaflet-kmz'] = {})); }(this, (function (exports) { 'use strict'; // import JSZip from 'jszip'; // import * as toGeoJSON from '@tmcw/togeojson'; function loadFile(url) { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); xhr.responseType = "arraybuffer"; xhr.onload = () => { if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 0)) { resolve(xhr.response || xhr.responseText); } else { resolve(''); } }; xhr.onerror = () => reject("Error " + xhr.status + " while fetching remote file: " + url); xhr.send(); }); } function getKmlDoc(files) { return files["doc.kml"] ? "doc.kml" : getKmlFiles(Object.keys(files))[0]; } function getKmlFiles(files) { return files.filter((file) => isKmlFile(file)); } function getImageFiles(files) { return files.filter((file) => isImageFile(file)); } function getFileExt(filename) { return filename.split('.').pop().toLowerCase().replace('jpg', 'jpeg'); } function getFileName(url) { return url.split('/').pop(); } function getMimeType(filename, ext) { var mime = 'text/plain'; if (/\.(jpe?g|png|gif|bmp)$/i.test(filename)) { mime = 'image/' + ext; } else if (/\.kml$/i.test(filename)) { mime = 'text/plain'; } return mime; } function isImageFile(filename) { return /\.(jpe?g|png|gif|bmp)$/i.test(filename); } function isKmlFile(filename) { return /.*\.kml/.test(filename); } /** * It checks if a given file begins with PK, if so it's zipped * * @link https://en.wikipedia.org/wiki/List_of_file_signatures */ function isZipped(file) { return 'PK' === String.fromCharCode(new Uint8Array(file, 0, 1), new Uint8Array(file, 1, 1)); } function lazyLoader(urls, promise) { return promise instanceof Promise ? promise : Promise.all(urls.map(url => loadJS(url))) } function loadJS(url) { return new Promise((resolve, reject) => { let tag = document.createElement("script"); tag.addEventListener('load', resolve.bind(url), { once: true }); tag.src = url; document.head.appendChild(tag); }); } function parseLatLonBox(xml) { let box = L.latLngBounds([ xml.getElementsByTagName('south')[0].childNodes[0].nodeValue, xml.getElementsByTagName('west')[0].childNodes[0].nodeValue ], [ xml.getElementsByTagName('north')[0].childNodes[0].nodeValue, xml.getElementsByTagName('east')[0].childNodes[0].nodeValue ]); let rotation = xml.getElementsByTagName('rotation')[0]; if (rotation !== undefined) { rotation = parseFloat(rotation.childNodes[0].nodeValue); } return [box, rotation]; } function parseGroundOverlay(xml, props) { let [bounds, rotation] = parseLatLonBox(xml.getElementsByTagName('LatLonBox')[0]); let href = xml.getElementsByTagName('href')[0]; let color = xml.getElementsByTagName('color')[0]; let icon = xml.getElementsByTagName('Icon')[0]; let options = {}; if (!href && icon) { href = icon.getElementsByTagName('href')[0]; } href = href.childNodes[0].nodeValue; href = props.icons[href] || href; if (color) { color = color.childNodes[0].nodeValue; options.opacity = parseInt(color.substring(0, 2), 16) / 255.0; options.color = '#' + color.substring(6, 8) + color.substring(4, 6) + color.substring(2, 4); } if (rotation) { options.rotation = rotation; } return new L.KMZImageOverlay(href, bounds, { opacity: options.opacity, angle: options.rotation }); } function toGeoJSON(data, props) { var xml = data instanceof XMLDocument ? data : toXML(data); var json = window.toGeoJSON.kml(xml); json.properties = L.extend({}, json.properties, props || {}); return json; } function toXML(data) { var text = data; if (data instanceof ArrayBuffer) { text = new Uint8Array(data).reduce(function (data, byte) { return data + String.fromCharCode(byte); }, ''); var encoding = text.substring(0, text.indexOf("?>")).match(/encoding\s*=\s*["'](.*)["']/i); if (encoding) { text = new TextDecoder(encoding[1]).decode(data); } } else { text = text.substr(text.indexOf('<')); } return text ? (new DOMParser()).parseFromString(text, 'text/xml') : document.implementation.createDocument(null, "kml");} function unzip(folder) { return new Promise((resolve, reject) => { window.JSZip.loadAsync(folder) .then((zip) => { // Parse KMZ files. var files = Object.keys(zip.files) .map((name) => { var entry = zip.files[name]; if (isImageFile(name)) { var ext = getFileExt(name); var mime = getMimeType(name, ext); return entry .async("base64") .then((value) => [name, 'data:' + mime + ';base64,' + value]); } return entry .async("text") .then((value) => [name, value]); // [ fileName, stringValue ] }); // Return KMZ files. Promise.all(files).then((list) => resolve(list.reduce((obj, item) => { obj[item[0]] = item[1]; // { fileName: stringValue } return obj; }, {})) ); }); }); } const KMZLayer = L.KMZLayer = L.FeatureGroup.extend({ options: { interactive: true, ballon: true, bindPopup: true, bindTooltip: true, preferCanvas: false, }, initialize: function(kmzUrl, options) { L.extend(this.options, options); if (L.Browser.mobile) this.options.bindTooltip = false; this._layers = {}; if (kmzUrl) this.load(kmzUrl); }, add: function(kmzUrl) { this.load(kmzUrl); }, load: function(kmzUrl) { L.KMZLayer._jsPromise = lazyLoader(this._requiredJSModules(), L.KMZLayer._jsPromise) .then(() => loadFile(kmzUrl)) .then((data) => this.parse(data, { name: getFileName(kmzUrl), icons: {} })); }, parse: function(data, props) { return isZipped(data) ? this._parseKMZ(data, props) : this._parseKML(data, props); }, _parseKMZ: function(data, props) { unzip(data).then((kmzFiles) => { var kmlDoc = getKmlDoc(kmzFiles); var images = getImageFiles(Object.keys(kmzFiles)); var kmlString = kmzFiles[kmlDoc]; // cache all images with their base64 encoding props.icons = images.reduce((obj, item) => { obj[item] = kmzFiles[item]; return obj; }, {}); this._parseKML(kmlString, props); }); }, _parseKML: function(data, props) { var xml = toXML(data); var geojson = toGeoJSON(xml, props); var layer = (this.options.geometryToLayer || this._geometryToLayer).call(this, geojson, xml); this.addLayer(layer); this.fire('load', { layer: layer, name: geojson.properties.name }); }, _geometryToLayer: function(data, xml) { var preferCanvas = this._map ? this._map.options.preferCanvas : this.options.preferCanvas; // var emptyIcon = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'/%3E"; // parse GeoJSON //console.log("DATA",data) var layer = L.geoJson(data, { pointToLayer: (feature, latlng) => { //console.log("FEAT",feature) if (preferCanvas) { return L.kmzMarker(latlng, { iconUrl: data.properties.icons[feature.properties.icon] || feature.properties.icon, iconSize: [28, 28], iconAnchor: [14, 14], interactive: this.options.interactive, }); } var kicon = L.icon({ iconUrl: data.properties.icons[feature.properties.icon] || feature.properties.icon, iconSize: [28, 28], iconAnchor: [14, 14], }) if (feature.properties && feature.properties.SymbolSpecification) { var mysymbol; if (feature.properties.MilitaryEchelon && feature.properties.SymbolSpecification.split(':')[0] === "APP-6D") { var a = feature.properties.SymbolSpecification.split(':')[1].substr(0,8); var c = feature.properties.SymbolSpecification.split(':')[1].substr(10); var b = parseInt(feature.properties.MilitaryEchelon) + 10; mysymbol = new ms.Symbol("" + a + b + c); } else { mysymbol = new ms.Symbol(feature.properties.SymbolSpecification.split(':')[1]); } mysymbol = mysymbol.setOptions({ size:20 }); if (feature.properties.name) { mysymbol = mysymbol.setOptions({ uniqueDesignation:feature.properties.name }); } if (feature.properties.MilitaryParentId) { mysymbol = mysymbol.setOptions({ higherFormation:feature.properties.MilitaryParentId }); } kicon = L.icon({ iconUrl: mysymbol.toDataURL(), iconAnchor: [mysymbol.getAnchor().x, mysymbol.getAnchor().y], }); //console.log("META",mysymbol.getMetadata()) } if (kicon.options.iconUrl === undefined) { // No icon found so just use default marker. return L.marker(latlng); } else { return L.marker(latlng, { icon: kicon, interactive: this.options.interactive, }); } // TODO: handle L.svg renderer within the L.KMZMarker class? }, style: (feature) => { //console.log("FEATSTYLE",feature) var styles = {}; var prop = feature.properties; if (prop.stroke) { styles.stroke = true; styles.color = prop.stroke; } if (prop.fill) { styles.fill = true; styles.fillColor = prop.fill; } if (prop["stroke-opacity"]) { styles.opacity = prop["stroke-opacity"]; } if (prop["fill-opacity"]) { styles.fillOpacity = prop["fill-opacity"]; } if (prop["stroke-width"]) { styles.weight = prop["stroke-width"] * 1.05; } return styles; }, onEachFeature: (feature, layer) => { //console.log("POP",feature.properties) //if (!this.options.ballon) return; var prop = feature.properties; var name = (prop.name || "").trim(); // var desc = (prop.description || "").trim(); var desc = prop.description || ""; var p = '
'+key+' | '+value+' |