您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
geoKMZer is a JavaScript library designed to convert KMZ into KML files, use with GeoKMLer to convert to GeoJSON.
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/527113/1538395/GeoKMZer.js
// ==UserScript== // @name GeoKMZer // @namespace https://github.com/JS55CT // @description geoKMZer is a JavaScript library designed to convert KMZ into KML files, use with GeoKMLer to convert to GeoJSON. // @version 1.1.0 // @author JS55CT // @license MIT // @match *://this-library-is-not-supposed-to-run.com/* // ==/UserScript== /*********************************************************** * ## Project Home < https://github.com/JS55CT/GeoKMLer > * MIT License * Copyright (c) 2025 Justin * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. **************************************************************/ var GeoKMZer = (function () { /** * GeoKMZer constructor function, which optionally wraps an object. * @param {Object} [obj] - Optional object to wrap. * @returns {GeoKMZer} - An instance of GeoKMZer. */ function GeoKMZer(obj) { if (obj instanceof GeoKMZer) return obj; if (!(this instanceof GeoKMZer)) return new GeoKMZer(obj); this._wrapped = obj; // Optional: wrap any input object if needed } /** * Converts a buffer of various types to a Uint8Array. * @param {ArrayBuffer|TypedArray} buffer - The buffer to convert. * @returns {Uint8Array} - The converted Uint8Array. * @throws Will throw an error if the buffer is not a valid buffer-like object. */ function toUint8Array(buffer) { if (!buffer) { throw new Error("forgot to pass buffer"); } if (ArrayBuffer.isView(buffer)) { // Buffer is a typed array view like Uint8Array return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength); } if (buffer instanceof ArrayBuffer) { // Buffer is an ArrayBuffer return new Uint8Array(buffer); } throw new Error("invalid buffer like object"); } /** * Yields entries from a ZIP archive contained in a buffer. * @generator * @param {Uint8Array} buffer - The buffer representing the ZIP file. * @yields {Object} - An object containing filename, comment, and a read() method to get file content. */ GeoKMZer.prototype.parseZipEntries = function* (buffer) { const textDecoder = new TextDecoder(); const decodeText = (buffer) => textDecoder.decode(buffer); const findEndOfCentralDirectory = (buffer) => { let offset = buffer.length - 20; const minSearchOffset = Math.max(offset - 65516, 2); while ((offset = buffer.lastIndexOf(80, offset - 1)) !== -1 && !(buffer[offset + 1] === 75 && buffer[offset + 2] === 5 && buffer[offset + 3] === 6) && offset > minSearchOffset); return offset; }; const throwError = (message) => { throw new Error("unzip-error: " + message); }; // Declare the decompression handling function let decompressWithDecompressionStream; const compressionFormat = "deflate-raw"; try { new self.DecompressionStream(compressionFormat); decompressWithDecompressionStream = async (compressedData) => { const decompressionStream = new self.DecompressionStream(compressionFormat); const writer = decompressionStream.writable.getWriter(); const reader = decompressionStream.readable.getReader(); writer.write(compressedData); writer.close(); const decompressedChunks = []; let totalLength = 0; let position = 0; let readResult; while (!(readResult = await reader.read()).done) { const chunk = readResult.value; decompressedChunks.push(chunk); totalLength += chunk.length; } if (decompressedChunks.length > 1) { const combinedArray = new Uint8Array(totalLength); for (const chunk of decompressedChunks) { combinedArray.set(chunk, position); position += chunk.length; } return combinedArray; } else { return decompressedChunks[0]; } }; } catch { console.error("DecompressionStream is unsupported or initialization failed."); } let centralDirectoryEnd = findEndOfCentralDirectory(buffer); if (centralDirectoryEnd === -1) { throwError(2); } const subarray = (start, length) => buffer.subarray((centralDirectoryEnd += start), (centralDirectoryEnd += length)); const dataView = new DataView(buffer.buffer, buffer.byteOffset); const getUint16 = (offset) => dataView.getUint16(offset + centralDirectoryEnd, true); const getUint32 = (offset) => dataView.getUint32(offset + centralDirectoryEnd, true); let numberOfEntries = getUint16(10); if (numberOfEntries !== getUint16(8)) { throwError(3); } centralDirectoryEnd = getUint32(16); while (numberOfEntries--) { let compressionType = getUint16(10), filenameLength = getUint16(28), extraFieldLength = getUint16(30), fileCommentLength = getUint16(32), compressedSize = getUint32(20), localHeaderOffset = getUint32(42), filename = decodeText(subarray(46, filenameLength)), comment = decodeText(subarray(extraFieldLength, fileCommentLength)), previousCentralDirectoryEnd = centralDirectoryEnd, compressedData; centralDirectoryEnd = localHeaderOffset; compressedData = subarray(30 + getUint16(26) + getUint16(28), compressedSize); yield { filename, comment, read: () => { if (compressionType & 8) { return decompressWithDecompressionStream(compressedData); } else if (compressionType) { throwError(1); } else { return compressedData; } }, }; centralDirectoryEnd = previousCentralDirectoryEnd; } }; /** * Unzips a KMZ buffer, potentially recursively, and retrieves contained KML files. * @param {ArrayBuffer|TypedArray} buffer - The buffer of the KMZ file. * @param {string} [parentFile=''] - Name of the parent file if dealing with nested KMZ files. * @returns {Object} - An object containing file names and their corresponding data buffers. * @throws Will throw an error if no KML files are found. */ GeoKMZer.prototype.unzipKMZ = async function (buffer, parentFile = "") { const files = {}; const kmlFileRegex = /.+\.kml$/i; const kmzFileRegex = /.+\.kmz$/i; const uint8Buffer = toUint8Array(buffer); for (const entry of this.parseZipEntries(uint8Buffer)) { if (kmlFileRegex.test(entry.filename)) { files[entry.filename] = await entry.read(); } else if (kmzFileRegex.test(entry.filename)) { // Handle nested KMZ file try { const nestedKMZBuffer = await entry.read(); const nestedFiles = await this.unzipKMZ(nestedKMZBuffer, entry.filename); Object.assign(files, nestedFiles); // Merge files found in nested archives } catch (nestedError) { console.error(`Error reading nested KMZ file "${entry.filename}":`, nestedError); } } } if (Object.keys(files).length === 0) { throw new Error("No KML file found in the KMZ archive."); } return files; }; /** * Reads a KMZ buffer and extracts KML files into an array of textual contents. * @param {ArrayBuffer|TypedArray} buffer - The buffer of the KMZ file. * @returns {Array} - An array of objects, each containing the filename and content of a KML file. * @throws Will log errors if any occur during KMZ reading. */ GeoKMZer.prototype.read = async function (buffer) { try { const kmlFiles = await this.unzipKMZ(buffer); const textDecoder = new TextDecoder(); const kmlContentsArray = []; for (const [kmlFilename, kmlBuffer] of Object.entries(kmlFiles)) { const kmlContent = textDecoder.decode(kmlBuffer); // Decode the KML buffer to text kmlContentsArray.push({ filename: kmlFilename, content: kmlContent }); // Store each content with its filename } return kmlContentsArray; } catch (error) { console.error("Error during KMZ reading:", error); } }; return GeoKMZer; })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址