kaax-mcp 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Analysis-file helpers — surface every downloadable artefact attached to
3
+ * a Kaax analysis (reports zip, density maps, line maps, the KML of the
4
+ * field that produced it) and stream them to disk. The MCP receives these
5
+ * URLs as part of the regular `POST /api/v0/analyses` response; we just
6
+ * pull them out by type and download with the existing safe-URL helper.
7
+ *
8
+ * Security: every URL goes through `assertSafeHttpUrl` (http(s) only,
9
+ * no shell metacharacters), filenames pass through `safeFileName` so a
10
+ * compromised server cannot make the MCP write outside the user's
11
+ * chosen directory.
12
+ */
13
+ import type { AnalysisRecord } from "./types.js";
14
+ /** What a single downloadable file looks like to the caller. */
15
+ export interface AvailableFile {
16
+ /** Logical key the user references: "resending.allReports", "kml", … */
17
+ key: string;
18
+ /** Short label for prompts — what KIND of file this is. */
19
+ label: string;
20
+ /** Remote URL or `inline` if the content is embedded in the analysis doc. */
21
+ url?: string;
22
+ /** Filename hint we want to use locally. */
23
+ fileName: string;
24
+ /** Approximate file type so the user / agent knows what to expect. */
25
+ kind: "zip" | "kml" | "image" | "shapefile" | "other";
26
+ }
27
+ /** Categorises every downloadable artefact attached to one analysis. */
28
+ export declare function listAvailableFiles(record: AnalysisRecord): AvailableFile[];
29
+ /** Result entry per file the downloader processed. */
30
+ export interface DownloadedFile {
31
+ key: string;
32
+ outputPath: string;
33
+ bytesWritten: number;
34
+ /**
35
+ * For ZIPs we also extract — `extractedTo` lists the entries written
36
+ * to the sibling directory `<name>_extracted/`.
37
+ */
38
+ extractedTo?: string;
39
+ extractedFiles?: string[];
40
+ }
41
+ /**
42
+ * Downloads each requested file from a Kaax analysis to a local directory.
43
+ * If a file is a ZIP we also unzip it next to the archive so the agent
44
+ * can immediately reference the contained SHP/KML/PDFs without a second
45
+ * tool call.
46
+ *
47
+ * - `record` – the analysis returned by `client.listAnalyses(...)`
48
+ * - `outputDir` – directory to write into (created if absent)
49
+ * - `keys` – optional whitelist of file keys (from listAvailableFiles)
50
+ */
51
+ export declare function downloadAnalysisFiles(record: AnalysisRecord, outputDir: string, keys?: string[]): Promise<DownloadedFile[]>;
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Analysis-file helpers — surface every downloadable artefact attached to
3
+ * a Kaax analysis (reports zip, density maps, line maps, the KML of the
4
+ * field that produced it) and stream them to disk. The MCP receives these
5
+ * URLs as part of the regular `POST /api/v0/analyses` response; we just
6
+ * pull them out by type and download with the existing safe-URL helper.
7
+ *
8
+ * Security: every URL goes through `assertSafeHttpUrl` (http(s) only,
9
+ * no shell metacharacters), filenames pass through `safeFileName` so a
10
+ * compromised server cannot make the MCP write outside the user's
11
+ * chosen directory.
12
+ */
13
+ import AdmZip from "adm-zip";
14
+ import { writeFile } from "fs/promises";
15
+ import { join } from "path";
16
+ import { downloadFile, safeFileName } from "./platform.js";
17
+ /** Categorises every downloadable artefact attached to one analysis. */
18
+ export function listAvailableFiles(record) {
19
+ const out = [];
20
+ const recordAny = record;
21
+ const reports = recordAny.reports ?? {};
22
+ // The full-bundle ZIPs (most useful — they contain SHP + KML + PDFs).
23
+ if (reports.resending?.allReports) {
24
+ out.push({
25
+ key: "resending.allReports",
26
+ label: "Replanting full report (zip with SHP + PDF)",
27
+ url: reports.resending.allReports,
28
+ fileName: "resending_reports.zip",
29
+ kind: "zip",
30
+ });
31
+ }
32
+ if (reports.counting?.allReports) {
33
+ out.push({
34
+ key: "counting.allReports",
35
+ label: "Counting full report (zip with SHP + PDF)",
36
+ url: reports.counting.allReports,
37
+ fileName: "counting_reports.zip",
38
+ kind: "zip",
39
+ });
40
+ }
41
+ if (reports.path?.allReports) {
42
+ out.push({
43
+ key: "path.allReports",
44
+ label: "Path full report (zip)",
45
+ url: reports.path.allReports,
46
+ fileName: "path_reports.zip",
47
+ kind: "zip",
48
+ });
49
+ }
50
+ // Individual raster maps.
51
+ if (reports.resending?.densityMap) {
52
+ out.push({
53
+ key: "resending.densityMap",
54
+ label: "Replanting density map",
55
+ url: reports.resending.densityMap,
56
+ fileName: "resending_density.png",
57
+ kind: "image",
58
+ });
59
+ }
60
+ if (reports.resending?.resendingLineMap) {
61
+ out.push({
62
+ key: "resending.lineMap",
63
+ label: "Replanting line map",
64
+ url: reports.resending.resendingLineMap,
65
+ fileName: "resending_lines.png",
66
+ kind: "image",
67
+ });
68
+ }
69
+ if (reports.counting?.densityMap) {
70
+ out.push({
71
+ key: "counting.densityMap",
72
+ label: "Counting density map",
73
+ url: reports.counting.densityMap,
74
+ fileName: "counting_density.png",
75
+ kind: "image",
76
+ });
77
+ }
78
+ if (reports.counting?.sizeDistributionMap) {
79
+ out.push({
80
+ key: "counting.sizeMap",
81
+ label: "Counting size-distribution map",
82
+ url: reports.counting.sizeDistributionMap,
83
+ fileName: "counting_size.png",
84
+ kind: "image",
85
+ });
86
+ }
87
+ if (reports.path?.densityMap) {
88
+ out.push({
89
+ key: "path.densityMap",
90
+ label: "Path density map",
91
+ url: reports.path.densityMap,
92
+ fileName: "path_density.png",
93
+ kind: "image",
94
+ });
95
+ }
96
+ if (reports.path?.pathDistributionMap) {
97
+ out.push({
98
+ key: "path.distributionMap",
99
+ label: "Path distribution map",
100
+ url: reports.path.pathDistributionMap,
101
+ fileName: "path_distribution.png",
102
+ kind: "image",
103
+ });
104
+ }
105
+ // KML — embedded inline as text rather than a remote URL.
106
+ if (typeof record.kml === "string" && record.kml.length > 0) {
107
+ out.push({
108
+ key: "kml",
109
+ label: "Field boundary (KML — inline, written from the analysis doc)",
110
+ fileName: "field.kml",
111
+ kind: "kml",
112
+ });
113
+ }
114
+ return out;
115
+ }
116
+ /**
117
+ * Downloads each requested file from a Kaax analysis to a local directory.
118
+ * If a file is a ZIP we also unzip it next to the archive so the agent
119
+ * can immediately reference the contained SHP/KML/PDFs without a second
120
+ * tool call.
121
+ *
122
+ * - `record` – the analysis returned by `client.listAnalyses(...)`
123
+ * - `outputDir` – directory to write into (created if absent)
124
+ * - `keys` – optional whitelist of file keys (from listAvailableFiles)
125
+ */
126
+ export async function downloadAnalysisFiles(record, outputDir, keys) {
127
+ const all = listAvailableFiles(record);
128
+ const wanted = keys && keys.length > 0 ? all.filter((f) => keys.includes(f.key)) : all;
129
+ const results = [];
130
+ for (const f of wanted) {
131
+ const localName = safeFileName(f.fileName);
132
+ const destPath = join(outputDir, localName);
133
+ if (f.kind === "kml") {
134
+ // KML lives inline in the analysis doc; just write it.
135
+ const body = record.kml;
136
+ if (!body)
137
+ continue;
138
+ await writeFile(destPath, body, "utf8");
139
+ results.push({
140
+ key: f.key,
141
+ outputPath: destPath,
142
+ bytesWritten: Buffer.byteLength(body, "utf8"),
143
+ });
144
+ continue;
145
+ }
146
+ if (!f.url)
147
+ continue;
148
+ // Streamed download (size-bounded via 5-min timeout in downloadFile).
149
+ const dl = await downloadFile(f.url, destPath);
150
+ const entry = {
151
+ key: f.key,
152
+ outputPath: dl.outputPath,
153
+ bytesWritten: dl.bytesWritten,
154
+ };
155
+ // Best-effort unzip for full-report archives.
156
+ if (f.kind === "zip") {
157
+ try {
158
+ const extractDir = destPath.replace(/\.zip$/i, "_extracted");
159
+ const zip = new AdmZip(dl.outputPath);
160
+ zip.extractAllTo(extractDir, /* overwrite */ true);
161
+ const entries = zip
162
+ .getEntries()
163
+ .filter((e) => !e.isDirectory)
164
+ .map((e) => join(extractDir, e.entryName));
165
+ entry.extractedTo = extractDir;
166
+ entry.extractedFiles = entries;
167
+ }
168
+ catch (err) {
169
+ // Non-fatal — the user still has the zip on disk and can extract
170
+ // by hand if our extractor chokes on a quirky archive.
171
+ process.stderr.write(`[kaax-mcp] zip extract failed for ${dl.outputPath}: ${err.message}\n`);
172
+ }
173
+ }
174
+ results.push(entry);
175
+ }
176
+ return results;
177
+ }
178
+ //# sourceMappingURL=analysis-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analysis-files.js","sourceRoot":"","sources":["../src/analysis-files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * GIS file I/O — read/write Shapefile, KML and GeoJSON locally.
3
+ *
4
+ * All in-memory representations use **GeoJSON** so every GIS operation
5
+ * downstream (turf, shp-write, KML serialisation) speaks the same dialect.
6
+ *
7
+ * Coordinate reference: everything stays in WGS84 / EPSG:4326. Kaax does
8
+ * not support other CRS, and turf operations assume lon/lat input. If a
9
+ * Shapefile carries a sibling .prj that is not WGS84 we log a warning
10
+ * but read the coordinates as-is — re-projecting in pure JS without a
11
+ * proj4 dependency would be a footgun.
12
+ */
13
+ import type { FeatureCollection, Geometry } from "geojson";
14
+ export type AnyFeatureCollection = FeatureCollection<Geometry, Record<string, unknown>>;
15
+ /**
16
+ * Read every feature from a `.shp` (+ sibling `.dbf` for attributes) into
17
+ * an in-memory GeoJSON FeatureCollection. The `.shp` extension is added
18
+ * automatically if the caller passes the base name.
19
+ */
20
+ export declare function readShapefile(shpPath: string): Promise<AnyFeatureCollection>;
21
+ /**
22
+ * Write a FeatureCollection to disk as a Shapefile **bundle**.
23
+ *
24
+ * shp-write packages SHP + SHX + DBF + PRJ into one ZIP, which is the
25
+ * portable shape ESRI ships and what every QGIS / ArcGIS user expects.
26
+ * If you need the bare `.shp` next to other tools, unzip it after.
27
+ *
28
+ * The output path is treated as the base name — we append `.zip` ourselves
29
+ * so callers can't accidentally clobber a SHP alongside the archive.
30
+ */
31
+ export declare function writeShapefileZip(fc: AnyFeatureCollection, outputBasePath: string): Promise<string>;
32
+ /** Read a `.geojson` / `.json` file into a FeatureCollection. */
33
+ export declare function readGeoJSON(path: string): Promise<AnyFeatureCollection>;
34
+ /** Write a FeatureCollection to a `.geojson` file (pretty-printed). */
35
+ export declare function writeGeoJSON(fc: AnyFeatureCollection, outputPath: string): Promise<string>;
36
+ /**
37
+ * Write a FeatureCollection as KML 2.2. Supports Point, MultiPoint,
38
+ * LineString, MultiLineString, Polygon and MultiPolygon. Any feature
39
+ * properties become `<ExtendedData>` so the data round-trips through
40
+ * QGIS / Google Earth without loss.
41
+ */
42
+ export declare function writeKML(fc: AnyFeatureCollection, outputPath: string, options?: {
43
+ documentName?: string;
44
+ }): Promise<string>;
45
+ /** Detect format from extension. Returns `"unknown"` if we don't handle it. */
46
+ export declare function detectFormat(path: string): "shp" | "kml" | "geojson" | "unknown";
47
+ /**
48
+ * Read any supported GIS file into a FeatureCollection. Used by tools so
49
+ * the user can pass `.shp`, `.kml` or `.geojson` interchangeably.
50
+ */
51
+ export declare function readAny(path: string): Promise<AnyFeatureCollection>;
52
+ /**
53
+ * Resolve an output path, defaulting to alongside the input file with a
54
+ * suffix and the requested extension. Keeps the tool surface tidy when
55
+ * the caller didn't specify an explicit destination.
56
+ */
57
+ export declare function defaultOutputPath(inputPath: string, suffix: string, ext: string): string;
package/dist/gis-io.js ADDED
@@ -0,0 +1,270 @@
1
+ /**
2
+ * GIS file I/O — read/write Shapefile, KML and GeoJSON locally.
3
+ *
4
+ * All in-memory representations use **GeoJSON** so every GIS operation
5
+ * downstream (turf, shp-write, KML serialisation) speaks the same dialect.
6
+ *
7
+ * Coordinate reference: everything stays in WGS84 / EPSG:4326. Kaax does
8
+ * not support other CRS, and turf operations assume lon/lat input. If a
9
+ * Shapefile carries a sibling .prj that is not WGS84 we log a warning
10
+ * but read the coordinates as-is — re-projecting in pure JS without a
11
+ * proj4 dependency would be a footgun.
12
+ */
13
+ import { open as openShapefile } from "shapefile";
14
+ import shpwrite from "@mapbox/shp-write";
15
+ import { create as xmlCreate } from "xmlbuilder2";
16
+ import { existsSync } from "fs";
17
+ import { readFile, writeFile } from "fs/promises";
18
+ import { basename, dirname, extname, join } from "path";
19
+ // ─────────────────────────────────────────────────────────────────────────
20
+ // Shapefile (read)
21
+ // ─────────────────────────────────────────────────────────────────────────
22
+ /**
23
+ * Read every feature from a `.shp` (+ sibling `.dbf` for attributes) into
24
+ * an in-memory GeoJSON FeatureCollection. The `.shp` extension is added
25
+ * automatically if the caller passes the base name.
26
+ */
27
+ export async function readShapefile(shpPath) {
28
+ const finalPath = shpPath.toLowerCase().endsWith(".shp")
29
+ ? shpPath
30
+ : `${shpPath}.shp`;
31
+ if (!existsSync(finalPath)) {
32
+ throw new Error(`Shapefile not found: ${finalPath}`);
33
+ }
34
+ const dbfPath = finalPath.replace(/\.shp$/i, ".dbf");
35
+ const features = [];
36
+ // The shapefile package exposes an iterator on the returned source
37
+ // (`source.read()`), not a top-level `read(source)`. Different versions
38
+ // of @types/shapefile typed this differently; we cast to keep the call
39
+ // site portable.
40
+ const source = (await openShapefile(finalPath, existsSync(dbfPath) ? dbfPath : undefined, { encoding: "utf8" }));
41
+ try {
42
+ while (true) {
43
+ const next = await source.read();
44
+ if (next.done)
45
+ break;
46
+ const feat = next.value;
47
+ if (feat?.geometry)
48
+ features.push(feat);
49
+ }
50
+ }
51
+ finally {
52
+ if (typeof source.close === "function")
53
+ await source.close();
54
+ }
55
+ return {
56
+ type: "FeatureCollection",
57
+ features,
58
+ };
59
+ }
60
+ // ─────────────────────────────────────────────────────────────────────────
61
+ // Shapefile (write)
62
+ // ─────────────────────────────────────────────────────────────────────────
63
+ /**
64
+ * Write a FeatureCollection to disk as a Shapefile **bundle**.
65
+ *
66
+ * shp-write packages SHP + SHX + DBF + PRJ into one ZIP, which is the
67
+ * portable shape ESRI ships and what every QGIS / ArcGIS user expects.
68
+ * If you need the bare `.shp` next to other tools, unzip it after.
69
+ *
70
+ * The output path is treated as the base name — we append `.zip` ourselves
71
+ * so callers can't accidentally clobber a SHP alongside the archive.
72
+ */
73
+ export async function writeShapefileZip(fc, outputBasePath) {
74
+ const out = outputBasePath.toLowerCase().endsWith(".zip")
75
+ ? outputBasePath
76
+ : `${outputBasePath}.zip`;
77
+ // shp-write returns the ZIP as an ArrayBuffer / Uint8Array. Force Buffer
78
+ // for Node fs.
79
+ const ab = (await shpwrite.zip(fc));
80
+ const buf = Buffer.isBuffer(ab)
81
+ ? ab
82
+ : Buffer.from(ab instanceof ArrayBuffer ? ab : ab.buffer);
83
+ await writeFile(out, buf);
84
+ return out;
85
+ }
86
+ // ─────────────────────────────────────────────────────────────────────────
87
+ // GeoJSON
88
+ // ─────────────────────────────────────────────────────────────────────────
89
+ /** Read a `.geojson` / `.json` file into a FeatureCollection. */
90
+ export async function readGeoJSON(path) {
91
+ if (!existsSync(path))
92
+ throw new Error(`GeoJSON not found: ${path}`);
93
+ const raw = await readFile(path, "utf8");
94
+ const parsed = JSON.parse(raw);
95
+ if (parsed.type === "FeatureCollection") {
96
+ return parsed;
97
+ }
98
+ if (parsed.type === "Feature") {
99
+ return {
100
+ type: "FeatureCollection",
101
+ features: [parsed],
102
+ };
103
+ }
104
+ throw new Error(`Unexpected GeoJSON shape at ${path}: must be FeatureCollection or Feature`);
105
+ }
106
+ /** Write a FeatureCollection to a `.geojson` file (pretty-printed). */
107
+ export async function writeGeoJSON(fc, outputPath) {
108
+ await writeFile(outputPath, JSON.stringify(fc, null, 2), "utf8");
109
+ return outputPath;
110
+ }
111
+ // ─────────────────────────────────────────────────────────────────────────
112
+ // KML
113
+ // ─────────────────────────────────────────────────────────────────────────
114
+ /**
115
+ * Write a FeatureCollection as KML 2.2. Supports Point, MultiPoint,
116
+ * LineString, MultiLineString, Polygon and MultiPolygon. Any feature
117
+ * properties become `<ExtendedData>` so the data round-trips through
118
+ * QGIS / Google Earth without loss.
119
+ */
120
+ export async function writeKML(fc, outputPath, options = {}) {
121
+ const docName = options.documentName ?? basename(outputPath, ".kml");
122
+ const root = xmlCreate({ version: "1.0", encoding: "UTF-8" })
123
+ .ele("kml", { xmlns: "http://www.opengis.net/kml/2.2" })
124
+ .ele("Document")
125
+ .ele("name")
126
+ .txt(docName)
127
+ .up();
128
+ for (const f of fc.features) {
129
+ const placemark = root.ele("Placemark");
130
+ const nameProp = (f.properties?.name ?? f.properties?.Name ?? "");
131
+ if (nameProp)
132
+ placemark.ele("name").txt(String(nameProp)).up();
133
+ // Attributes — preserve everything.
134
+ if (f.properties && Object.keys(f.properties).length > 0) {
135
+ const ext = placemark.ele("ExtendedData");
136
+ for (const [k, v] of Object.entries(f.properties)) {
137
+ if (v === undefined || v === null)
138
+ continue;
139
+ ext.ele("Data", { name: k }).ele("value").txt(String(v)).up().up();
140
+ }
141
+ }
142
+ appendKmlGeometry(placemark, f.geometry);
143
+ }
144
+ const xml = root.end({ prettyPrint: true });
145
+ await writeFile(outputPath, xml, "utf8");
146
+ return outputPath;
147
+ }
148
+ function appendKmlGeometry(parent, geom) {
149
+ switch (geom.type) {
150
+ case "Point":
151
+ parent
152
+ .ele("Point")
153
+ .ele("coordinates")
154
+ .txt(geom.coordinates.slice(0, 2).join(","))
155
+ .up()
156
+ .up();
157
+ break;
158
+ case "MultiPoint": {
159
+ const multi = parent.ele("MultiGeometry");
160
+ for (const c of geom.coordinates) {
161
+ multi
162
+ .ele("Point")
163
+ .ele("coordinates")
164
+ .txt(c.slice(0, 2).join(","))
165
+ .up()
166
+ .up();
167
+ }
168
+ break;
169
+ }
170
+ case "LineString":
171
+ parent
172
+ .ele("LineString")
173
+ .ele("coordinates")
174
+ .txt(geom.coordinates.map((c) => c.slice(0, 2).join(",")).join(" "))
175
+ .up()
176
+ .up();
177
+ break;
178
+ case "MultiLineString": {
179
+ const multi = parent.ele("MultiGeometry");
180
+ for (const ls of geom.coordinates) {
181
+ multi
182
+ .ele("LineString")
183
+ .ele("coordinates")
184
+ .txt(ls.map((c) => c.slice(0, 2).join(",")).join(" "))
185
+ .up()
186
+ .up();
187
+ }
188
+ break;
189
+ }
190
+ case "Polygon":
191
+ appendPolygonKml(parent, geom.coordinates);
192
+ break;
193
+ case "MultiPolygon": {
194
+ const multi = parent.ele("MultiGeometry");
195
+ for (const poly of geom.coordinates) {
196
+ appendPolygonKml(multi, poly);
197
+ }
198
+ break;
199
+ }
200
+ default:
201
+ // Other geometry types (GeometryCollection) — skipped intentionally.
202
+ break;
203
+ }
204
+ }
205
+ function appendPolygonKml(parent, rings) {
206
+ const polyEl = parent.ele("Polygon");
207
+ const [outer, ...inner] = rings;
208
+ if (outer) {
209
+ polyEl
210
+ .ele("outerBoundaryIs")
211
+ .ele("LinearRing")
212
+ .ele("coordinates")
213
+ .txt(outer.map((c) => c.slice(0, 2).join(",")).join(" "))
214
+ .up()
215
+ .up()
216
+ .up();
217
+ }
218
+ for (const ring of inner) {
219
+ polyEl
220
+ .ele("innerBoundaryIs")
221
+ .ele("LinearRing")
222
+ .ele("coordinates")
223
+ .txt(ring.map((c) => c.slice(0, 2).join(",")).join(" "))
224
+ .up()
225
+ .up()
226
+ .up();
227
+ }
228
+ }
229
+ // ─────────────────────────────────────────────────────────────────────────
230
+ // Format dispatch
231
+ // ─────────────────────────────────────────────────────────────────────────
232
+ /** Detect format from extension. Returns `"unknown"` if we don't handle it. */
233
+ export function detectFormat(path) {
234
+ const ext = extname(path).toLowerCase();
235
+ if (ext === ".shp" || ext === ".zip")
236
+ return "shp";
237
+ if (ext === ".kml")
238
+ return "kml";
239
+ if (ext === ".geojson" || ext === ".json")
240
+ return "geojson";
241
+ return "unknown";
242
+ }
243
+ /**
244
+ * Read any supported GIS file into a FeatureCollection. Used by tools so
245
+ * the user can pass `.shp`, `.kml` or `.geojson` interchangeably.
246
+ */
247
+ export async function readAny(path) {
248
+ const fmt = detectFormat(path);
249
+ switch (fmt) {
250
+ case "shp":
251
+ return readShapefile(path);
252
+ case "kml":
253
+ throw new Error("KML read is not yet implemented in gis-io. For now, convert KML to GeoJSON externally or pass a shapefile / geojson.");
254
+ case "geojson":
255
+ return readGeoJSON(path);
256
+ default:
257
+ throw new Error(`Unsupported GIS format for ${path}. Supported: .shp, .geojson, .json.`);
258
+ }
259
+ }
260
+ /**
261
+ * Resolve an output path, defaulting to alongside the input file with a
262
+ * suffix and the requested extension. Keeps the tool surface tidy when
263
+ * the caller didn't specify an explicit destination.
264
+ */
265
+ export function defaultOutputPath(inputPath, suffix, ext) {
266
+ const dir = dirname(inputPath);
267
+ const base = basename(inputPath, extname(inputPath));
268
+ return join(dir, `${base}${suffix}${ext.startsWith(".") ? ext : `.${ext}`}`);
269
+ }
270
+ //# sourceMappingURL=gis-io.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gis-io.js","sourceRoot":"","sources":["../src/gis-io.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,IAAI,IAAI,aAAa,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,QAAQ,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAIxD,4EAA4E;AAC5E,mBAAmB;AACnB,4EAA4E;AAE5E;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAe;IAEf,MAAM,SAAS,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;QACtD,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,GAAG,OAAO,MAAM,CAAC;IACrB,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,wBAAwB,SAAS,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAiD,EAAE,CAAC;IAElE,mEAAmE;IACnE,wEAAwE;IACxE,uEAAuE;IACvE,iBAAiB;IACjB,MAAM,MAAM,GAAG,CAAC,MAAM,aAAa,CACjC,SAAS,EACT,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EACzC,EAAE,QAAQ,EAAE,MAAM,EAAE,CACrB,CAGA,CAAC;IAEF,IAAI,CAAC;QACH,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACjC,IAAI,IAAI,CAAC,IAAI;gBAAE,MAAM;YACrB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAmD,CAAC;YACtE,IAAI,IAAI,EAAE,QAAQ;gBAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;YAAS,CAAC;QACT,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,UAAU;YAAE,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IAC/D,CAAC;IAED,OAAO;QACL,IAAI,EAAE,mBAAmB;QACzB,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,oBAAoB;AACpB,4EAA4E;AAE5E;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,EAAwB,EACxB,cAAsB;IAEtB,MAAM,GAAG,GAAG,cAAc,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;QACvD,CAAC,CAAC,cAAc;QAChB,CAAC,CAAC,GAAG,cAAc,MAAM,CAAC;IAE5B,yEAAyE;IACzE,eAAe;IACf,MAAM,EAAE,GAAG,CAAC,MAAO,QAKjB,CAAC,GAAG,CAAC,EAAE,CAAC,CAA6B,CAAC;IACxC,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,YAAY,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAE,EAAiB,CAAC,MAAM,CAAC,CAAC;IAE5E,MAAM,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC1B,OAAO,GAAG,CAAC;AACb,CAAC;AAED,4EAA4E;AAC5E,UAAU;AACV,4EAA4E;AAE5E,iEAAiE;AACjE,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAY;IAEZ,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC;IACrE,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAEiB,CAAC;IAC/C,IAAK,MAA+B,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;QAClE,OAAO,MAA8B,CAAC;IACxC,CAAC;IACD,IAAK,MAAkB,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC3C,OAAO;YACL,IAAI,EAAE,mBAAmB;YACzB,QAAQ,EAAE,CAAC,MAAoD,CAAC;SACjE,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,KAAK,CACb,+BAA+B,IAAI,wCAAwC,CAC5E,CAAC;AACJ,CAAC;AAED,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,EAAwB,EACxB,UAAkB;IAElB,MAAM,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACjE,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,4EAA4E;AAC5E,MAAM;AACN,4EAA4E;AAE5E;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,EAAwB,EACxB,UAAkB,EAClB,UAAqC,EAAE;IAEvC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,IAAI,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACrE,MAAM,IAAI,GAAG,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;SAC1D,GAAG,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,gCAAgC,EAAE,CAAC;SACvD,GAAG,CAAC,UAAU,CAAC;SACf,GAAG,CAAC,MAAM,CAAC;SACX,GAAG,CAAC,OAAO,CAAC;SACZ,EAAE,EAAE,CAAC;IAER,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC;QAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,IAAI,IAAI,CAAC,CAAC,UAAU,EAAE,IAAI,IAAI,EAAE,CAAW,CAAC;QAC5E,IAAI,QAAQ;YAAE,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QAC/D,oCAAoC;QACpC,IAAI,CAAC,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzD,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YAC1C,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC;gBAClD,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI;oBAAE,SAAS;gBAC5C,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YACrE,CAAC;QACH,CAAC;QACD,iBAAiB,CAAC,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,MAAM,SAAS,CAAC,UAAU,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO,UAAU,CAAC;AACpB,CAAC;AAQD,SAAS,iBAAiB,CAAC,MAAc,EAAE,IAAc;IACvD,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,OAAO;YACV,MAAM;iBACH,GAAG,CAAC,OAAO,CAAC;iBACZ,GAAG,CAAC,aAAa,CAAC;iBAClB,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;iBAC3C,EAAE,EAAE;iBACJ,EAAE,EAAE,CAAC;YACR,MAAM;QACR,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;YAC1C,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjC,KAAK;qBACF,GAAG,CAAC,OAAO,CAAC;qBACZ,GAAG,CAAC,aAAa,CAAC;qBAClB,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;qBAC5B,EAAE,EAAE;qBACJ,EAAE,EAAE,CAAC;YACV,CAAC;YACD,MAAM;QACR,CAAC;QACD,KAAK,YAAY;YACf,MAAM;iBACH,GAAG,CAAC,YAAY,CAAC;iBACjB,GAAG,CAAC,aAAa,CAAC;iBAClB,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;iBACnE,EAAE,EAAE;iBACJ,EAAE,EAAE,CAAC;YACR,MAAM;QACR,KAAK,iBAAiB,CAAC,CAAC,CAAC;YACvB,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;YAC1C,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBAClC,KAAK;qBACF,GAAG,CAAC,YAAY,CAAC;qBACjB,GAAG,CAAC,aAAa,CAAC;qBAClB,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;qBACrD,EAAE,EAAE;qBACJ,EAAE,EAAE,CAAC;YACV,CAAC;YACD,MAAM;QACR,CAAC;QACD,KAAK,SAAS;YACZ,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;YAC3C,MAAM;QACR,KAAK,cAAc,CAAC,CAAC,CAAC;YACpB,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;YAC1C,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBACpC,gBAAgB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAChC,CAAC;YACD,MAAM;QACR,CAAC;QACD;YACE,qEAAqE;YACrE,MAAM;IACV,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAc,EAAE,KAAmB;IAC3D,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACrC,MAAM,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC;IAChC,IAAI,KAAK,EAAE,CAAC;QACV,MAAM;aACH,GAAG,CAAC,iBAAiB,CAAC;aACtB,GAAG,CAAC,YAAY,CAAC;aACjB,GAAG,CAAC,aAAa,CAAC;aAClB,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;aACxD,EAAE,EAAE;aACJ,EAAE,EAAE;aACJ,EAAE,EAAE,CAAC;IACV,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM;aACH,GAAG,CAAC,iBAAiB,CAAC;aACtB,GAAG,CAAC,YAAY,CAAC;aACjB,GAAG,CAAC,aAAa,CAAC;aAClB,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;aACvD,EAAE,EAAE;aACJ,EAAE,EAAE;aACJ,EAAE,EAAE,CAAC;IACV,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,kBAAkB;AAClB,4EAA4E;AAE5E,+EAA+E;AAC/E,MAAM,UAAU,YAAY,CAC1B,IAAY;IAEZ,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IACxC,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,MAAM;QAAE,OAAO,KAAK,CAAC;IACnD,IAAI,GAAG,KAAK,MAAM;QAAE,OAAO,KAAK,CAAC;IACjC,IAAI,GAAG,KAAK,UAAU,IAAI,GAAG,KAAK,OAAO;QAAE,OAAO,SAAS,CAAC;IAC5D,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAY;IACxC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAC/B,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,KAAK;YACR,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC;QAC7B,KAAK,KAAK;YACR,MAAM,IAAI,KAAK,CACb,sHAAsH,CACvH,CAAC;QACJ,KAAK,SAAS;YACZ,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC;QAC3B;YACE,MAAM,IAAI,KAAK,CACb,8BAA8B,IAAI,qCAAqC,CACxE,CAAC;IACN,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,SAAiB,EACjB,MAAc,EACd,GAAW;IAEX,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IACrD,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,MAAM,GAAG,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;AAC/E,CAAC"}