Skip to content

Commit a3e7934

Browse files
author
Phil Varner
authored
Merge pull request #17 from stac-api-extensions/pv/update-spatial-operators-bbox-or-point
change basic spatial operators defintion to only require bbox or point
2 parents 3dc3bac + fb24796 commit a3e7934

File tree

2 files changed

+100
-47
lines changed

2 files changed

+100
-47
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
11
# Changelog
2+
23
All notable changes to this project will be documented in this file.
34

45
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
56
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
67

8+
## [Unreleased] - TBD
9+
10+
### Changed
11+
12+
- Basic Spatial Operators in CQL2 now only requires BBOX and POINT support, so the text
13+
and examples were updated to account for this.
14+
715
## [v1.0.0-rc.2] - 2022-11-01
816

17+
### Changed
18+
919
- Update language on catalog-level queryables
1020
- Fix several examples
1121

README.md

Lines changed: 90 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
- [STAC API - Filter Extension Specification](#stac-api---filter-extension-specification)
44
- [Overview](#overview)
55
- [Limitations of Item Search](#limitations-of-item-search)
6-
- [Filter expressiveness](#filter-expressiveness)
6+
- [Filter Expressiveness](#filter-expressiveness)
77
- [Conformance Classes](#conformance-classes)
88
- [Getting Started with Implementation](#getting-started-with-implementation)
99
- [Queryables](#queryables)
@@ -28,27 +28,30 @@
2828
- [Example 6: Temporal Intersection](#example-6-temporal-intersection)
2929
- [Example 6: T\_INTERSECTS cql2-text (GET)](#example-6-t_intersects-cql2-text-get)
3030
- [Example 6: T\_INTERSECTS cql2-json (POST)](#example-6-t_intersects-cql2-json-post)
31-
- [Example 7: Spatial Intersection](#example-7-spatial-intersection)
31+
- [Example 7: Spatial Intersection in Basic Spatial Operators](#example-7-spatial-intersection-in-basic-spatial-operators)
3232
- [Example 7: S\_INTERSECTS cql2-text (GET)](#example-7-s_intersects-cql2-text-get)
3333
- [Example 7: S\_INTERSECTS cql2-json (POST)](#example-7-s_intersects-cql2-json-post)
34-
- [Example 8: Spatial Intersection Disjunction](#example-8-spatial-intersection-disjunction)
34+
- [Example 8: Spatial Intersection in Spatial Operators](#example-8-spatial-intersection-in-spatial-operators)
3535
- [Example 8: S\_INTERSECTS cql2-text (GET)](#example-8-s_intersects-cql2-text-get)
3636
- [Example 8: S\_INTERSECTS cql2-json (POST)](#example-8-s_intersects-cql2-json-post)
37-
- [Example 9: Using IS NULL](#example-9-using-is-null)
38-
- [Example 9: cql2-text (GET)](#example-9-cql2-text-get)
39-
- [Example 9: cql2-json (POST)](#example-9-cql2-json-post)
40-
- [Example 10: Using BETWEEN](#example-10-using-between)
37+
- [Example 9: Spatial Intersection Disjunction](#example-9-spatial-intersection-disjunction)
38+
- [Example 9: S\_INTERSECTS cql2-text (GET)](#example-9-s_intersects-cql2-text-get)
39+
- [Example 9: S\_INTERSECTS cql2-json (POST)](#example-9-s_intersects-cql2-json-post)
40+
- [Example 10: Using IS NULL](#example-10-using-is-null)
4141
- [Example 10: cql2-text (GET)](#example-10-cql2-text-get)
4242
- [Example 10: cql2-json (POST)](#example-10-cql2-json-post)
43-
- [Example 11: Using LIKE](#example-11-using-like)
43+
- [Example 11: Using BETWEEN](#example-11-using-between)
4444
- [Example 11: cql2-text (GET)](#example-11-cql2-text-get)
4545
- [Example 11: cql2-json (POST)](#example-11-cql2-json-post)
46-
- [Example 12: Using the CASEI Case-insensitive Comparison Function](#example-12-using-the-casei-case-insensitive-comparison-function)
46+
- [Example 12: Using LIKE](#example-12-using-like)
4747
- [Example 12: cql2-text (GET)](#example-12-cql2-text-get)
4848
- [Example 12: cql2-json (POST)](#example-12-cql2-json-post)
49-
- [Example 13: Using the ACCENTI Accent-insensitive Comparison Function](#example-13-using-the-accenti-accent-insensitive-comparison-function)
49+
- [Example 13: Using the CASEI Case-insensitive Comparison Function](#example-13-using-the-casei-case-insensitive-comparison-function)
5050
- [Example 13: cql2-text (GET)](#example-13-cql2-text-get)
5151
- [Example 13: cql2-json (POST)](#example-13-cql2-json-post)
52+
- [Example 14: Using the ACCENTI Accent-insensitive Comparison Function](#example-14-using-the-accenti-accent-insensitive-comparison-function)
53+
- [Example 14: cql2-text (GET)](#example-14-cql2-text-get)
54+
- [Example 14: cql2-json (POST)](#example-14-cql2-json-post)
5255

5356
## Overview
5457

@@ -114,6 +117,7 @@ OAFeat defines a limited set of filtering capabilities. Filtering can only be do
114117
with only a single `bbox` (rectangular spatial filter) parameter and a single datetime (instant or interval) parameter.
115118

116119
The STAC Item Search specification extends the functionality of OAFeat in a few key ways:
120+
117121
- It allows cross-collection filtering, whereas OAFeat only allows filtering within a single collection.
118122
(`collections` parameter, accepting 0 or more collections)
119123
- It allows filtering by Item ID (`ids` parameter)
@@ -123,7 +127,7 @@ However, it does not contain a formalized way to filter based on arbitrary field
123127
no way to express the filter "item.properties.eo:cloud_cover is less than 10". It also does not have a way to logically combine
124128
multiple spatial or temporal filters.
125129

126-
## Filter expressiveness
130+
## Filter Expressiveness
127131

128132
This extension expands the capabilities of Item Search and the OAFeat Items resource with
129133
[OAFeat Part 3 CQL2](https://portal.ogc.org/files/96288)
@@ -132,6 +136,7 @@ those provided by SQL. This extension also supports the Queryables mechanism tha
132136
predicates.
133137

134138
CQL2 enables more expressive queries than supported by STAC API Item Search. These include:
139+
135140
- Use of Item Property values in predicates (e.g., `item.properties.eo:cloud_cover`), using comparison operators
136141
- Items whose `datetime` values are in the month of August of the years 2017-2021, using OR and datetime comparisons
137142
- Items whose `geometry` values intersect any one of several Polygons, using `OR` and `S_INTERSECTS`
@@ -180,15 +185,20 @@ The implementation **may** support the OAFeat Part 3 *Features Filter* conforman
180185
CQL2 conformance classes to the Features resource(`/collections/{cid}/items`).
181186

182187
For additional capabilities, the following classes may be implemented:
188+
183189
- Advanced Comparison Operators
184190
(`http://www.opengis.net/spec/cql2/1.0/conf/advanced-comparison-operators`) defines the `LIKE`,
185191
`BETWEEN`, and `IN` operators. **Note**: this conformance class no longer requires implementing the
186192
`lower` and `upper` functions.
187-
- Basic Spatial Operators (`http://www.opengis.net/spec/cql2/1.0/conf/basic-spatial-operators`) defines the intersects operator (`S_INTERSECTS`).
193+
- Basic Spatial Operators (`http://www.opengis.net/spec/cql2/1.0/conf/basic-spatial-operators`) defines the intersects operator (`S_INTERSECTS`)
194+
that accepts only a BBOX or POINT parameter.
188195
- Spatial Operators
189196
(`http://www.opengis.net/spec/cql2/1.0/conf/spatial-operators`) defines a set of operators that
190197
are part of the Dimensionally Extended Nine-intersection Model (DE-9IM) relation operators
191-
(`S_CONTAINS`, `S_CROSSES`, `S_DISJOINT`, `S_EQUALS`, `S_INTERSECTS`, `S_OVERLAPS`, `S_TOUCHES`, and `S_WITHIN`)
198+
(`S_CONTAINS`, `S_CROSSES`, `S_DISJOINT`, `S_EQUALS`, `S_INTERSECTS`, `S_OVERLAPS`, `S_TOUCHES`, and `S_WITHIN`),
199+
and additionally defines the intersects operator (`S_INTERSECTS`) to accept LINESTRING,
200+
POLYGON, MULTIPOINT, MULTILINESTRING, and MULTIPOLYGON,
201+
in addition to BBOX and POINT as supported in Basic Spatial Operators.
192202
- Temporal Operators
193203
(`http://www.opengis.net/spec/cql2/1.0/conf/temporal-operators`) defines several temporal
194204
operators that provide more expressivity with datetime types than the relative comparison
@@ -226,11 +236,13 @@ dynamic Queryables schema.
226236
Formal definitions and grammars for CQL2 can be found in the
227237
[OAFeat CQL spec](https://github.com/opengeospatial/ogcapi-features/tree/master/cql2) includes a BNF grammar
228238
for CQL2 Text and both a JSON Schema and an OpenAPI specification for CQL2 JSON. The standalone files are:
239+
229240
- [cql.bnf](https://github.com/opengeospatial/ogcapi-features/blob/master/extensions/cql/standard/schema/cql.bnf)
230241
- [cql.json](https://github.com/opengeospatial/ogcapi-features/blob/master/extensions/cql/standard/schema/cql.json)
231242
- [cql.yml](https://github.com/opengeospatial/ogcapi-features/blob/master/extensions/cql/standard/schema/cql.yml)
232243

233244
These projects have or are developing CQL2 support:
245+
234246
- [pgstac](https://github.com/stac-utils/pgstac) supports CQL2 JSON
235247
- [pygeofilter](https://github.com/geopython/pygeofilter) has support for CQL2 JSON and for the older ECQL standard that
236248
- [xtraplatform-spatial](https://github.com/interactive-instruments/xtraplatform-spatial) has support for CQL2 Text and provides an [ANTLR 4 grammer](https://github.com/interactive-instruments/xtraplatform-spatial/tree/master/xtraplatform-cql/src/main/antlr/de/ii/xtraplatform/cql/infra)
@@ -487,7 +499,7 @@ This example uses the queryables definition in (Interaction with Endpoints)(#int
487499
Note that `filter-lang` defaults to `cql2-text` in this case. The parameter `filter-crs` defaults
488500
to `http://www.opengis.net/def/crs/OGC/1.3/CRS84` for a STAC API.
489501

490-
```
502+
```text
491503
filter=id='LC08_L1TP_060247_20180905_20180912_01_T1_L1TP' AND collection='landsat8_l1tp'
492504
```
493505

@@ -523,7 +535,7 @@ OGC API Features filters only operate against a single collection already.
523535

524536
#### Example 2: GET with cql2-text
525537

526-
```
538+
```text
527539
filter=collection = 'landsat8_l1tp'
528540
AND eo:cloud_cover <= 10
529541
AND datetime >= TIMESTAMP('2021-04-08T04:39:23Z')
@@ -675,7 +687,7 @@ a tiny sliver of data.
675687

676688
#### Example 3: AND cql2-text (GET)
677689

678-
```
690+
```text
679691
filter=sentinel:data_coverage > 50 AND eo:cloud_cover < 10
680692
```
681693

@@ -709,7 +721,7 @@ This uses the same queryables as Example 3.
709721

710722
#### Example 4: OR cql2-text (GET)
711723

712-
```
724+
```text
713725
filter=sentinel:data_coverage > 50 OR eo:cloud_cover < 10
714726
```
715727

@@ -765,7 +777,7 @@ This queryables JSON Schema is used in these examples:
765777

766778
#### Example 5: GET with cql2-text
767779

768-
```
780+
```text
769781
filter=prop1 = prop2
770782
```
771783

@@ -793,7 +805,7 @@ have any overlap between them.
793805

794806
#### Example 6: T_INTERSECTS cql2-text (GET)
795807

796-
```
808+
```text
797809
filter=T_INTERSECTS(datetime, INTERVAL('2020-11-11T00:00:00Z', '2020-11-12T00:00:00Z'))
798810
```
799811

@@ -812,20 +824,51 @@ filter=T_INTERSECTS(datetime, INTERVAL('2020-11-11T00:00:00Z', '2020-11-12T00:00
812824
}
813825
```
814826

815-
### Example 7: Spatial Intersection
827+
### Example 7: Spatial Intersection in Basic Spatial Operators
816828

817829
The only spatial operator that must be implemented for Basic Spatial Operators
818-
is `S_INTERSECTS`. This has the same semantics as provided
830+
is `S_INTERSECTS` supporting BBOX and POINT. This has the same semantics as provided
819831
by the Item Search `intersects` parameter. The `cql2-text` format uses WKT geometries and the `cql2-json`
820832
format uses GeoJSON geometries.
821833

822834
#### Example 7: S_INTERSECTS cql2-text (GET)
823835

836+
```text
837+
filter=S_INTERSECTS(geometry,POINT(-77.0824 38.7886))
824838
```
839+
840+
#### Example 7: S_INTERSECTS cql2-json (POST)
841+
842+
```json
843+
{
844+
"filter-lang": "cql2-json",
845+
"filter": {
846+
"op": "s_intersects",
847+
"args": [
848+
{ "property": "geometry" } ,
849+
{
850+
"type": "Point",
851+
"coordinates": [-77.0824, 38.7886]
852+
}
853+
]
854+
}
855+
}
856+
```
857+
858+
### Example 8: Spatial Intersection in Spatial Operators
859+
860+
The Spatial Operators extends the Basic Spatial Operators by adding support for additional
861+
geometries to the `S_INTERSECTS` parameter. This has the same semantics as provided
862+
by the Item Search `intersects` parameter. The `cql2-text` format uses WKT geometries and the `cql2-json`
863+
format uses GeoJSON geometries.
864+
865+
#### Example 8: S_INTERSECTS cql2-text (GET)
866+
867+
```text
825868
filter=S_INTERSECTS(geometry,POLYGON((-77.0824 38.7886,-77.0189 38.7886,-77.0189 38.8351,-77.0824 38.8351,-77.0824 38.7886)))
826869
```
827870

828-
#### Example 7: S_INTERSECTS cql2-json (POST)
871+
#### Example 8: S_INTERSECTS cql2-json (POST)
829872

830873
```json
831874
{
@@ -847,20 +890,20 @@ filter=S_INTERSECTS(geometry,POLYGON((-77.0824 38.7886,-77.0189 38.7886,-77.0189
847890
}
848891
```
849892

850-
### Example 8: Spatial Intersection Disjunction
893+
### Example 9: Spatial Intersection Disjunction
851894

852895
One limitation of the `intersects` parameter is that only a single geometry may be provided. While most
853896
GeoJSON geometries can be combined to form a composite (e.g., multiple Polygons can be combined to form a
854897
MultiPolygon), this is much easier to do in the query by combining `S_INTERSECTS` predicates with the `OR`
855898
logical operator.
856899

857-
#### Example 8: S_INTERSECTS cql2-text (GET)
900+
#### Example 9: S_INTERSECTS cql2-text (GET)
858901

859-
```
902+
```text
860903
filter=S_INTERSECTS(geometry,POLYGON((-77.0824 38.7886,-77.0189 38.7886,-77.0189 38.8351,-77.0824 38.8351,-77.0824 38.7886))) OR S_INTERSECTS(geometry,POLYGON((-79.0935 38.7886,-79.0290 38.7886,-79.0290 38.8351,-79.0935 38.8351,-79.0935 38.7886)))
861904
```
862905

863-
#### Example 8: S_INTERSECTS cql2-json (POST)
906+
#### Example 9: S_INTERSECTS cql2-json (POST)
864907

865908
```json
866909
{
@@ -901,7 +944,7 @@ filter=S_INTERSECTS(geometry,POLYGON((-77.0824 38.7886,-77.0189 38.7886,-77.0189
901944
}
902945
```
903946

904-
### Example 9: Using IS NULL
947+
### Example 10: Using IS NULL
905948

906949
One of the main use cases for STAC API is doing cross-collection query. Commonly, this means that items have
907950
different sets of properties. For example, a collection of Sentinel 2 data may have a property
@@ -910,13 +953,13 @@ different sets of properties. For example, a collection of Sentinel 2 data may h
910953
data. However, we many also want to also include in our result items that do not have a value defined for
911954
either of those properties.
912955

913-
#### Example 9: cql2-text (GET)
956+
#### Example 10: cql2-text (GET)
914957

915-
```
958+
```text
916959
filter=sentinel:data_coverage > 50 OR landsat:coverage_percent < 10 OR (sentinel:data_coverage IS NULL AND landsat:coverage_percent IS NULL)
917960
```
918961

919-
#### Example 9: cql2-json (POST)
962+
#### Example 10: cql2-json (POST)
920963

921964
```json
922965
{
@@ -950,17 +993,17 @@ filter=sentinel:data_coverage > 50 OR landsat:coverage_percent < 10 OR (sentinel
950993
}
951994
```
952995

953-
### Example 10: Using BETWEEN
996+
### Example 11: Using BETWEEN
954997

955998
The BETWEEN operator allows for checking if a numeric value is within a specified inclusive range.
956999

957-
#### Example 10: cql2-text (GET)
1000+
#### Example 11: cql2-text (GET)
9581001

959-
```
1002+
```text
9601003
filter=eo:cloud_cover BETWEEN 0 AND 50
9611004
```
9621005

963-
#### Example 10: cql2-json (POST)
1006+
#### Example 11: cql2-json (POST)
9641007

9651008
```json
9661009
{
@@ -975,17 +1018,17 @@ filter=eo:cloud_cover BETWEEN 0 AND 50
9751018
}
9761019
```
9771020

978-
### Example 11: Using LIKE
1021+
### Example 12: Using LIKE
9791022

9801023
The LIKE operator allows for pattern-based string matching.
9811024

982-
#### Example 11: cql2-text (GET)
1025+
#### Example 12: cql2-text (GET)
9831026

984-
```
1027+
```text
9851028
filter=mission LIKE 'sentinel%'
9861029
```
9871030

988-
#### Example 11: cql2-json (POST)
1031+
#### Example 12: cql2-json (POST)
9891032

9901033
```json
9911034
{
@@ -1000,7 +1043,7 @@ filter=mission LIKE 'sentinel%'
10001043
}
10011044
```
10021045

1003-
### Example 12: Using the CASEI Case-insensitive Comparison Function
1046+
### Example 13: Using the CASEI Case-insensitive Comparison Function
10041047

10051048
The predefined function `CASEI` allows for case-insensitive comparisons. This function is
10061049
defined in the Accent and Case-insensitive Comparison conformance class.
@@ -1009,17 +1052,17 @@ In the example using 'Straße', both the capitalized 'S' and Eszett ('ß') are c
10091052
insensitive representation whereby the expressions `CASEI('Straße')`, `CASEI('straße')`,
10101053
`CASEI('Strasse')`, and `CASEI('strasse')` are all equal.
10111054

1012-
#### Example 12: cql2-text (GET)
1055+
#### Example 13: cql2-text (GET)
10131056

1014-
```
1057+
```text
10151058
filter=CASEI(provider) = CASEI('coolsat')
10161059
```
10171060

1018-
```
1061+
```text
10191062
filter=CASEI(provider) = CASEI('Straße')
10201063
```
10211064

1022-
#### Example 12: cql2-json (POST)
1065+
#### Example 13: cql2-json (POST)
10231066

10241067
```json
10251068
{
@@ -1059,19 +1102,19 @@ filter=CASEI(provider) = CASEI('Straße')
10591102
}
10601103
```
10611104

1062-
### Example 13: Using the ACCENTI Accent-insensitive Comparison Function
1105+
### Example 14: Using the ACCENTI Accent-insensitive Comparison Function
10631106

10641107
The predefined function `ACCENTI` allows for accent-insensitive comparisons. This function is
10651108
defined in the Accent and Case-insensitive Comparison conformance class. In the example below,
10661109
`ACCENTI('tiburon')` and `ACCENTI('tiburón')` evaluate to be equal.
10671110

1068-
#### Example 13: cql2-text (GET)
1111+
#### Example 14: cql2-text (GET)
10691112

1070-
```
1113+
```text
10711114
filter=ACCENTI(provider) = ACCENTI('tiburón')
10721115
```
10731116

1074-
#### Example 13: cql2-json (POST)
1117+
#### Example 14: cql2-json (POST)
10751118

10761119
```json
10771120
{

0 commit comments

Comments
 (0)