pygeofilter
pygeofilter is a pure Python library for parsing and evaluating OGC filtering standards, including Filter Encoding 2.0 and CQL2. It provides robust tools to parse filter expressions from various formats (e.g., CQL2-TEXT, CQL2-JSON) and evaluate them against Python data structures or translate them into SQL queries. The current version is 0.3.3, and it receives active development with regular patch and minor releases.
Common errors
-
ModuleNotFoundError: No module named 'pygeofilter.parsers.cql2'
cause You are attempting to import from an old or incorrect path for CQL2 parsers. The `cql2` module was refactored into format-specific parsers.fixUse `from pygeofilter.parsers.cql2_text import parse` for text-based CQL2 filters or `from pygeofilter.parsers.cql2_json import parse` for JSON-based CQL2 filters. -
lark.exceptions.UnexpectedInput: No rule matches the current input
cause The filter expression provided is syntactically incorrect according to the OGC standard (e.g., CQL2-TEXT) or contains elements not supported by the parser's grammar.fixCarefully review your filter string for typos, incorrect operators, mismatched parentheses, or unsupported functions. Ensure it strictly adheres to the specified OGC filter standard (e.g., CQL2-TEXT specification) that the parser expects. -
sqlalchemy.exc.CompileError: (in _create_bind_param) Type annotation for parameter '...' must be a SQLAlchemy Type, not 'datetime'
cause When translating a filter to a SQLAlchemy expression, a type mismatch occurred between a Python value in your filter and what SQLAlchemy expects for a column type or bind parameter. This is common with `datetime` or other complex types.fixEnsure the Python data types used in your filter values are compatible with the corresponding SQLAlchemy column types. For `datetime` objects, ensure consistent timezone awareness (or naivety) and proper handling during the `to_sqlalchemy` conversion, possibly by providing explicit type mappers if needed for custom types.
Warnings
- breaking Versions of `pygeofilter` prior to 0.3.0 (specifically 0.2.x releases) explicitly pinned the `SQLAlchemy` dependency to version `< 2.0.0`. Attempting to use these older `pygeofilter` versions with `SQLAlchemy` 2.x will lead to runtime errors, particularly when using SQL backend generation features. The explicit pin was removed in 0.3.0, implying compatibility with SQLAlchemy 2.x.
- gotcha As of `pygeofilter` v0.3.3, the use of `ILIKE` (case-insensitive `LIKE`) in SQL backend generation has become opt-in. If your filter expressions previously used `LIKE` and implicitly relied on case-insensitivity when generating SQL queries (e.g., for PostgreSQL), those queries might now be case-sensitive. This change was implemented to align better with SQL standards where `LIKE` is typically case-sensitive unless collation specifies otherwise.
- gotcha Incorrect or unsupported geometry encodings or Coordinate Reference Systems (CRS) in spatial filter predicates can lead to parsing errors or incorrect evaluation results. `pygeofilter` expects geometries in WKT (Well-Known Text) or GeoJSON representations for certain operations, and CRS handling might require careful input.
Install
-
pip install pygeofilter -
pip install pygeofilter[sql]
Imports
- parse
from pygeofilter.parsers.cql2 import parse
from pygeofilter.parsers.cql2_text import parse
- evaluate
from pygeofilter.evaluate import evaluate
- to_sqlalchemy
from pygeofilter.backends.sqlalchemy import to_sqlalchemy
Quickstart
from pygeofilter.parsers.cql2_text import parse
from pygeofilter.evaluate import evaluate
# Define a CQL2 filter expression
cql_filter_string = "(name = 'test-feature' AND temperature > 25) OR (id IN ('A1', 'B2'))"
# Parse the filter expression
parsed_filter = parse(cql_filter_string)
# Define a data item (dictionary representing feature properties)
data_item = {
"name": "test-feature",
"temperature": 28.5,
"id": "A1",
"timestamp": "2024-01-01T10:00:00Z"
}
# Evaluate the filter against the data item
result = evaluate(parsed_filter, data_item)
print(f"Filter expression: {cql_filter_string}")
print(f"Data item: {data_item}")
print(f"Evaluation result: {result}")
# Example with a different data item (should fail the filter)
data_item_fail = {
"name": "other-feature",
"temperature": 20.0,
"id": "C3",
"timestamp": "2024-01-01T11:00:00Z"
}
result_fail = evaluate(parsed_filter, data_item_fail)
print(f"Evaluation result for other item: {result_fail}")