Skip to content

Commit 5c03a7b

Browse files
committed
fix(cql2-text): align spatial grammar with CQL2 spec
Replace non-standard ENVELOPE geometry literal with spec-compliant BBOX(west, south, east, north) using comma-separated signed numbers. Remove non-spec RELATE and BBOX predicate rules from the CQL2 text grammar. The AST nodes (ast.Relate, ast.BBox) are preserved for use by other parsers (FES, ECQL).
1 parent f799198 commit 5c03a7b

3 files changed

Lines changed: 15 additions & 15 deletions

File tree

pygeofilter/parsers/cql2_text/grammar.lark

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,6 @@
8282

8383

8484
?spatial_predicate: _binary_spatial_predicate_func "(" expression "," expression ")" -> binary_spatial_predicate
85-
| "RELATE" "(" expression "," expression "," SINGLE_QUOTED ")" -> relate_spatial_predicate
86-
| _binary_spatial_predicate_func "(" expression "," bbox ")" -> binary_spatial_predicate
8785

8886
!_binary_spatial_predicate_func: "S_INTERSECTS"i
8987
| "S_DISJOINT"i
@@ -120,17 +118,14 @@ func.2: attribute "(" expression ("," expression)* ")" -> function
120118
| BOOLEAN
121119
| SINGLE_QUOTED
122120
| ewkt_geometry -> geometry
123-
| envelope
124-
| bbox
121+
| bbox_literal
125122

126123
?full_number: number
127124
| "-" number -> neg
128125

129126
?number: FLOAT | INT
130127

131-
envelope: "ENVELOPE"i "(" number number number number ")"
132-
133-
bbox: "BBOX"i "(" full_number "," full_number "," full_number "," full_number ")"
128+
bbox_literal: "BBOX"i "(" full_number "," full_number "," full_number "," full_number ")"
134129

135130
BOOLEAN.2: ( "TRUE"i | "FALSE"i)
136131

pygeofilter/parsers/cql2_text/parser.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -132,19 +132,13 @@ def binary_temporal_predicate(self, lhs, op, rhs):
132132
op = op.lower()
133133
return TEMPORAL_PREDICATES_MAP[op](lhs, rhs)
134134

135-
def relate_spatial_predicate(self, lhs, rhs, pattern):
136-
return ast.Relate(lhs, rhs, pattern)
137-
138135
def distance_spatial_predicate(self, op, lhs, rhs, distance, units):
139136
cls = ast.DistanceWithin if op == "DWITHIN" else ast.DistanceBeyond
140137
return cls(lhs, rhs, distance, units)
141138

142139
def distance_units(self, value):
143140
return value
144141

145-
def bbox_spatial_predicate(self, lhs, minx, miny, maxx, maxy, crs=None):
146-
return ast.BBox(lhs, minx, miny, maxx, maxy, crs)
147-
148142
def function(self, func_name, *expressions):
149143
name = func_name.name.lower()
150144
if name == "casei":
@@ -190,8 +184,8 @@ def SINGLE_QUOTED(self, token):
190184
def geometry(self, value):
191185
return values.Geometry(value)
192186

193-
def bbox(self, x1, y1, x2, y2):
194-
return values.Envelope(x1, x2, y1, y2)
187+
def bbox_literal(self, west, south, east, north):
188+
return values.Envelope(west, east, south, north)
195189

196190
def interval(self, start, end):
197191
return values.Interval(start, end)

tests/parsers/cql2_text/test_parser.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,17 @@ def test_intersects_geometry():
259259
assert isinstance(result.rhs, values.Geometry)
260260

261261

262+
def test_intersects_bbox_literal():
263+
result = parse("S_INTERSECTS(attr, BBOX(-118, 33, -117, 34))")
264+
assert isinstance(result, ast.GeometryIntersects)
265+
assert result.lhs == ast.Attribute("attr")
266+
assert isinstance(result.rhs, values.Envelope)
267+
assert result.rhs.x1 == -118
268+
assert result.rhs.y1 == 33
269+
assert result.rhs.x2 == -117
270+
assert result.rhs.y2 == 34
271+
272+
262273
def test_attribute_boolean_literal_true():
263274
result = parse("attr = TRUE")
264275
assert result == ast.Equal(

0 commit comments

Comments
 (0)