cluster_points¶
The full machine-readable contract.
Input schema (JSON Schema)¶
{
"type": "object",
"required": ["geojson"],
"properties": {
"geojson": {
"type": "string",
"description": "A GeoJSON FeatureCollection of Point features."
},
"eps": {
"type": "number",
"default": 1000,
"minimum": 0,
"description": "Maximum metres between two points to be in the same neighbourhood."
},
"minPts": {
"type": "integer",
"default": 3,
"minimum": 1,
"description": "Minimum points to form a cluster."
}
}
}
Output¶
A GeoJSON FeatureCollection. Each Feature has:
| Field | Type | Description |
|---|---|---|
geometry.type |
string | Always "Polygon". |
geometry.coordinates |
array | 32-vertex regular polygon ring centred on the cluster centroid. |
properties.cluster_id |
integer | Stable per-response index, 0-based. |
properties.point_count |
integer | Number of input points in this cluster. |
properties.radius_meters |
number | Distance from centroid to the furthest member point. |
properties.kartoza_credit |
string | Provenance: "Processed by Kartoza.com tools". |
Algorithm¶
- Parse input GeoJSON into
orb.Pointslice. - Insert all points into a
quadtree.Quadtreekeyed by lon/lat. - In parallel (one goroutine per
numWorkers, default 8) compute each point'seps-neighbourhood usinggeo.Distancefor true geodesic filtering. - Run DBSCAN single-threaded, expanding clusters by reusing the pre-computed neighbours.
- For each cluster, compute centroid + max-radius and emit a 32-vertex polygon approximation of the bounding circle.
Complexity: O(N log N) average case, dominated by the parallel
neighbour pre-pass.
Edge cases¶
| Input | Behaviour |
|---|---|
| Empty FeatureCollection | "No points found in collection" text result. |
| Mix of Point and Polygon features | Polygons silently ignored. |
All points within eps of one another |
Single cluster. |
No two points within eps |
Zero clusters returned. |
minPts > len(points) |
Zero clusters returned. |
eps == 0 |
Zero clusters returned. |
| Invalid JSON | Error: "failed to parse GeoJSON: …" |