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 )
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
114117with only a single ` bbox ` (rectangular spatial filter) parameter and a single datetime (instant or interval) parameter.
115118
116119The 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
123127no way to express the filter "item.properties.eo: cloud_cover is less than 10". It also does not have a way to logically combine
124128multiple spatial or temporal filters.
125129
126- ## Filter expressiveness
130+ ## Filter Expressiveness
127131
128132This 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
132136predicates.
133137
134138CQL2 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
182187For 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.
226236Formal 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
233244These 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
487499Note that ` filter-lang ` defaults to ` cql2-text ` in this case. The parameter ` filter-crs ` defaults
488500to ` http://www.opengis.net/def/crs/OGC/1.3/CRS84 ` for a STAC API.
489501
490- ```
502+ ``` text
491503filter=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
527539filter=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
679691filter=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
713725filter=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
769781filter=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
797809filter=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
817829The 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
819831by the Item Search ` intersects ` parameter. The ` cql2-text ` format uses WKT geometries and the ` cql2-json `
820832format 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
825868filter=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
852895One limitation of the ` intersects ` parameter is that only a single geometry may be provided. While most
853896GeoJSON geometries can be combined to form a composite (e.g., multiple Polygons can be combined to form a
854897MultiPolygon), this is much easier to do in the query by combining ` S_INTERSECTS ` predicates with the ` OR `
855898logical operator.
856899
857- #### Example 8 : S_INTERSECTS cql2-text (GET)
900+ #### Example 9 : S_INTERSECTS cql2-text (GET)
858901
859- ```
902+ ``` text
860903filter=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
906949One of the main use cases for STAC API is doing cross-collection query. Commonly, this means that items have
907950different 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
910953data. However, we many also want to also include in our result items that do not have a value defined for
911954either of those properties.
912955
913- #### Example 9 : cql2-text (GET)
956+ #### Example 10 : cql2-text (GET)
914957
915- ```
958+ ``` text
916959filter=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
955998The 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
9601003filter=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
9801023The 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
9851028filter=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
10051048The predefined function ` CASEI ` allows for case-insensitive comparisons. This function is
10061049defined 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
10091052insensitive 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
10151058filter=CASEI(provider) = CASEI('coolsat')
10161059```
10171060
1018- ```
1061+ ``` text
10191062filter=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
10641107The predefined function ` ACCENTI ` allows for accent-insensitive comparisons. This function is
10651108defined 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
10711114filter=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