kaax-mcp 0.1.5 → 0.1.7

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,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"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * GIS operations — pure-JS implementations on top of `@turf/turf` so the
3
+ * MCP host machine does everything locally and the Kaax server never has
4
+ * to lift big geometry workloads. Returns FeatureCollections that gis-io
5
+ * writes out as SHP / KML / GeoJSON.
6
+ *
7
+ * Everything operates in WGS84 (lon/lat); distances are great-circle.
8
+ */
9
+ import type { AnyFeatureCollection } from "./gis-io.js";
10
+ /**
11
+ * Geometric intersection of every feature in `a` with every feature in
12
+ * `b`. Returns one feature per non-empty intersection, preserving the
13
+ * properties of A and prefixing B's properties with `b__` so attribute
14
+ * collisions don't silently overwrite.
15
+ *
16
+ * Useful for "show me which parcels of A fall inside the area of B".
17
+ */
18
+ export declare function intersectLayers(a: AnyFeatureCollection, b: AnyFeatureCollection): AnyFeatureCollection;
19
+ /**
20
+ * Geometric difference: every feature in `a` minus the union of features
21
+ * in `b`. Returns A's properties unchanged.
22
+ */
23
+ export declare function differenceLayers(a: AnyFeatureCollection, b: AnyFeatureCollection): AnyFeatureCollection;
24
+ /**
25
+ * Buffer every feature by `distanceMeters`. Negative values shrink
26
+ * polygons. Useful before splitting parcels — a 1 m negative buffer
27
+ * pulls geometries off shared edges so neighbour clips don't double-count.
28
+ */
29
+ export declare function bufferLayer(fc: AnyFeatureCollection, distanceMeters: number): AnyFeatureCollection;
30
+ /**
31
+ * Clip every feature in `layer` by the polygon mask. Returns only the
32
+ * portion of each feature that falls **inside** any feature of `mask`.
33
+ * For lines we use `lineSplit + booleanWithin`; for polygons we use
34
+ * `intersect`; for points we use `booleanPointInPolygon`.
35
+ */
36
+ export declare function clipByMask(layer: AnyFeatureCollection, mask: AnyFeatureCollection): AnyFeatureCollection;
37
+ /**
38
+ * Bin every feature in `layer` as "inside any polygon of `zones`" or
39
+ * "outside". Lines whose midpoint is in a zone are counted as inside.
40
+ * Returns two collections so the caller can write them to two SHPs.
41
+ */
42
+ export declare function extractFeaturesInPolygons(layer: AnyFeatureCollection, zones: AnyFeatureCollection): {
43
+ inside: AnyFeatureCollection;
44
+ outside: AnyFeatureCollection;
45
+ };
46
+ /**
47
+ * Split a FeatureCollection into one FeatureCollection per unique value of
48
+ * `fieldName`. Features without the field land under the special bucket
49
+ * `__missing__`. Useful for "I have one SHP with N parcels and I want one
50
+ * file per parcel".
51
+ */
52
+ export declare function splitByAttribute(fc: AnyFeatureCollection, fieldName: string): Map<string, AnyFeatureCollection>;
53
+ /**
54
+ * Add an `id` property to every feature. Modes:
55
+ * - "auto": sequential integers starting at 1
56
+ * - "from": copy the value of another field
57
+ * - "manual": caller supplies a `values` array of the same length
58
+ *
59
+ * Only adds; never overwrites an existing `id` unless `overwrite: true`.
60
+ * Kaax expects `id` to be present on every uploaded SHP, so this is the
61
+ * defensive tool that any agentic flow should call before uploading.
62
+ */
63
+ export declare function addIdField(fc: AnyFeatureCollection, opts: {
64
+ mode: "auto";
65
+ overwrite?: boolean;
66
+ } | {
67
+ mode: "from";
68
+ sourceField: string;
69
+ overwrite?: boolean;
70
+ } | {
71
+ mode: "manual";
72
+ values: (string | number)[];
73
+ overwrite?: boolean;
74
+ }): AnyFeatureCollection;
75
+ /**
76
+ * Filter features by a predicate over their properties. The expression
77
+ * is a very small whitelist DSL — only equality, AND, OR — to keep the
78
+ * MCP from evaluating arbitrary user JS.
79
+ *
80
+ * Examples:
81
+ * `codigo = 'CCM-12'`
82
+ * `region = 'escuintla' AND zafra = 2024`
83
+ * `state = 'active' OR state = 'pending'`
84
+ */
85
+ export declare function filterFeatures(fc: AnyFeatureCollection, expression: string): AnyFeatureCollection;
86
+ export interface ValidationIssue {
87
+ level: "error" | "warning";
88
+ code: string;
89
+ message: string;
90
+ featureIndex?: number;
91
+ }
92
+ /**
93
+ * Check a layer for the invariants Kaax expects on upload:
94
+ * - Geometries are Polygon or MultiPolygon (Kaax fields are parcels)
95
+ * - At least one feature
96
+ * - Every feature has a populated `id` property
97
+ * - `id` values are unique
98
+ * - Coordinates look like WGS84 (lon in [-180, 180], lat in [-90, 90])
99
+ * - Polygons close (first == last vertex) — turf usually tolerates open
100
+ * polygons but Kaax's ingestion is strict.
101
+ *
102
+ * Returns the list of issues found. Caller decides whether to abort, fix
103
+ * automatically, or surface to the user.
104
+ */
105
+ export declare function validateForKaax(fc: AnyFeatureCollection): ValidationIssue[];
106
+ /** Exported for tools that want to render summary statistics. */
107
+ export declare function summariseLayer(fc: AnyFeatureCollection): {
108
+ count: number;
109
+ byGeometry: Record<string, number>;
110
+ totalAreaHa?: number;
111
+ totalLengthM?: number;
112
+ };