|
1 | 1 | import type { RowGroup } from 'hyparquet' |
2 | | -import { bbox } from 'squirreling/src/spatial/bbox.js' |
3 | | -import { parseWkt } from 'squirreling/src/spatial/wkt.js' |
4 | 2 | import type { ExprNode } from 'squirreling/src/ast.js' |
5 | | -import type { BBox, Geometry, SimpleGeometry } from 'squirreling/src/spatial/geometry.js' |
| 3 | +import { bbox, decompose, parseWkt } from 'squirreling/src/spatial/index.js' |
| 4 | +import type { BoundingBox, Geometry } from 'squirreling/src/spatial/geometry.js' |
6 | 5 |
|
7 | 6 | export interface SpatialFilter { |
8 | 7 | column: string |
9 | | - queryBbox: BBox |
| 8 | + queryBbox: BoundingBox |
10 | 9 | } |
11 | 10 |
|
12 | 11 | /** |
@@ -40,35 +39,30 @@ function evaluateConstantGeom(node: ExprNode): Geometry | undefined { |
40 | 39 | return parseWkt(arg.value) ?? undefined |
41 | 40 | } |
42 | 41 | if (node.funcName === 'ST_MAKEENVELOPE') { |
43 | | - if (node.args.length < 4) return |
44 | | - const nums = node.args.slice(0, 4).map(a => a.type === 'literal' ? Number(a.value) : NaN) |
45 | | - if (nums.some(n => isNaN(n))) return |
46 | | - const [xmin = 0, ymin = 0, xmax = 0, ymax = 0] = nums |
| 42 | + if (node.args.length !== 4) return |
| 43 | + const nums = node.args.map(evaluateConstantNumber) as [number, number, number, number] |
| 44 | + if (nums.some(isNaN)) return |
| 45 | + const [xmin, ymin, xmax, ymax] = nums |
47 | 46 | return { |
48 | | - type: 'Polygon' as const, |
| 47 | + type: 'Polygon', |
49 | 48 | coordinates: [[[xmin, ymin], [xmax, ymin], [xmax, ymax], [xmin, ymax], [xmin, ymin]]], |
50 | 49 | } |
51 | 50 | } |
52 | 51 | } |
53 | 52 |
|
| 53 | +function evaluateConstantNumber(node: ExprNode): number { |
| 54 | + if (node.type !== 'literal') return NaN |
| 55 | + if (node.value === null) return NaN // Number(null) => 0 |
| 56 | + return Number(node.value) |
| 57 | +} |
| 58 | + |
54 | 59 | /** |
55 | 60 | * Compute the bounding box of any geometry type. |
56 | 61 | */ |
57 | | -function geomBbox(geom: Geometry): BBox | undefined { |
58 | | - if (geom.type === 'Point' || geom.type === 'LineString' || geom.type === 'Polygon') { |
59 | | - return bbox(geom) |
60 | | - } |
61 | | - let parts: SimpleGeometry[] |
62 | | - if (geom.type === 'MultiPoint') { |
63 | | - parts = geom.coordinates.map(c => ({ type: 'Point' as const, coordinates: c })) |
64 | | - } else if (geom.type === 'MultiLineString') { |
65 | | - parts = geom.coordinates.map(c => ({ type: 'LineString' as const, coordinates: c })) |
66 | | - } else if (geom.type === 'MultiPolygon') { |
67 | | - parts = geom.coordinates.map(c => ({ type: 'Polygon' as const, coordinates: c })) |
68 | | - } else { |
69 | | - return // GeometryCollection - not worth the complexity |
70 | | - } |
71 | | - if (!parts.length) return |
| 62 | +function geomBbox(geom: Geometry): BoundingBox | undefined { |
| 63 | + const parts = decompose(geom) |
| 64 | + if (parts.length === 0) return |
| 65 | + |
72 | 66 | let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity |
73 | 67 | for (const part of parts) { |
74 | 68 | const { minX: bMinX, minY: bMinY, maxX: bMaxX, maxY: bMaxY } = bbox(part) |
|
0 commit comments