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.
- package/dist/gis-io.d.ts +57 -0
- package/dist/gis-io.js +270 -0
- package/dist/gis-io.js.map +1 -0
- package/dist/gis-ops.d.ts +112 -0
- package/dist/gis-ops.js +495 -0
- package/dist/gis-ops.js.map +1 -0
- package/dist/gis-rows.d.ts +122 -0
- package/dist/gis-rows.js +348 -0
- package/dist/gis-rows.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/tools.js +545 -0
- package/dist/tools.js.map +1 -1
- package/package.json +1 -1
package/dist/gis-io.d.ts
ADDED
|
@@ -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":"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"}
|
|
@@ -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
|
+
};
|