Skip to content

Commit 4e08f65

Browse files
committed
add STAC API + CQL2 JSON merging
1 parent a7db843 commit 4e08f65

File tree

4 files changed

+84
-12
lines changed

4 files changed

+84
-12
lines changed

pycsw/core/pygeofilter_evaluate.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@
2929

3030
import logging
3131

32+
from shapely import box
3233
from shapely.geometry import shape
3334
from sqlalchemy import text
3435

3536
from pygeofilter import ast
37+
from pygeofilter.values import Envelope
3638
from pygeofilter.backends.evaluator import handle
3739
from pygeofilter.backends.sqlalchemy import filters
3840
from pygeofilter.backends.sqlalchemy.evaluate import SQLAlchemyFilterEvaluator
@@ -43,7 +45,8 @@
4345

4446

4547
class PycswFilterEvaluator(SQLAlchemyFilterEvaluator):
46-
def __init__(self, field_mapping=None, dbtype='sqlite', undefined_as_null=None):
48+
def __init__(self, field_mapping=None, dbtype='sqlite',
49+
undefined_as_null=None):
4750
super().__init__(field_mapping, undefined_as_null=undefined_as_null)
4851
self._pycsw_dbtype = dbtype
4952

@@ -56,7 +59,11 @@ def intersects(self, node, lhs, rhs):
5659
except AttributeError:
5760
crs = 4326
5861

59-
wkt = shape(node.rhs.geometry).wkt
62+
if isinstance(node.rhs, Envelope):
63+
wkt = box(node.rhs.x1, node.rhs.x2,
64+
node.rhs.y1, node.rhs.y2, ccw=False).wkt
65+
else:
66+
wkt = shape(node.rhs.geometry).wkt
6067

6168
if self._pycsw_dbtype == 'postgresql+postgis+native':
6269
return text(f"ST_Intersects({geometry}, 'SRID={crs};{wkt}')")

pycsw/ogc/pubsub/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def publish_message(pubsub_client, action: str, collection: str = None,
3939
"""
4040
Publish broker message
4141
42+
:param pubsub_client: `paho.mqtt.client.Client` instance
4243
:param action: `str` of action trigger name (create, update, delete)
4344
:param collection: `str` of collection identifier
4445
:param item: `str` of item identifier

pycsw/stac/api.py

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,8 @@ def items(self, headers_, json_post_data, args, collection='metadata:main'):
419419
:returns: tuple of headers, status code, content
420420
"""
421421

422+
cql_ops = []
423+
422424
if collection not in self.get_all_collections():
423425
msg = 'Invalid collection'
424426
LOGGER.exception(msg)
@@ -430,32 +432,93 @@ def items(self, headers_, json_post_data, args, collection='metadata:main'):
430432
LOGGER.debug('Empty JSON payload')
431433
json_post_data = {}
432434

433-
if 'filter' in json_post_data:
434-
LOGGER.debug('Detected filter query parameter')
435-
json_post_data = json_post_data.pop('filter')
436-
437435
if 'bbox' in json_post_data:
438436
LOGGER.debug('Detected bbox query parameter')
439-
bbox_ = json_post_data.pop('bbox')
440-
bbox_ = ','.join([str(b) for b in bbox_])
441-
args['bbox'] = bbox_
437+
438+
cql_ops.append({
439+
'op': 's_intersects', 'args': [{
440+
'property': 'geometry'
441+
},
442+
{'bbox': json_post_data.pop('bbox')}]
443+
})
442444

443445
if 'limit' in json_post_data:
444446
LOGGER.debug('Detected limit query parameter')
445447
args['limit'] = json_post_data.pop('limit')
446448

447449
if 'collections' in json_post_data:
448-
LOGGER.debug('Detected limit query parameter')
449-
args['collections'] = ','.join(json_post_data.pop('collections'))
450+
LOGGER.debug('Detected collections query parameter')
451+
cql_ops.append({
452+
'op': 'in',
453+
'args': [{
454+
'property': 'collections',
455+
},
456+
json_post_data.pop('collections')
457+
]
458+
})
450459

451460
if 'ids' in json_post_data:
452461
LOGGER.debug('Detected ids query parameter')
453-
args['ids'] = ','.join(json_post_data.pop('ids'))
462+
cql_ops.append({
463+
'op': 'in',
464+
'args': [{
465+
'property': 'identifier',
466+
},
467+
json_post_data.pop('ids')
468+
]
469+
})
454470

455471
if 'intersects' in json_post_data:
456472
LOGGER.debug('Detected intersects query parameter')
457473
# TODO
458474

475+
if 'datetime' in json_post_data:
476+
if '/' not in json_post_data['datetime']:
477+
cql_ops.append({
478+
'op': '=',
479+
'args': [
480+
{'property': 'date'},
481+
json_post_data.pop('datetime')
482+
]
483+
})
484+
else:
485+
begin, end = json_post_data.pop('datetime').split('/')
486+
if begin != '..':
487+
cql_ops.append({
488+
'op': '>=',
489+
'args': [
490+
{'property': 'time_begin'},
491+
begin
492+
]
493+
})
494+
if end != '..':
495+
cql_ops.append({
496+
'op': '<=',
497+
'args': [
498+
{'property': 'time_end'},
499+
end
500+
]
501+
})
502+
503+
if 'filter' in json_post_data:
504+
LOGGER.debug('Detected filter query parameter')
505+
json_post_data = json_post_data.pop('filter')
506+
507+
if not json_post_data and not cql_ops:
508+
LOGGER.debug('No JSON POST data or CQL ops')
509+
elif not json_post_data and cql_ops:
510+
LOGGER.debug('No JSON POST data left')
511+
json_post_data = {
512+
'op': 'and',
513+
'args': cql_ops
514+
}
515+
else:
516+
LOGGER.debug('JSON POST data is CQL2 JSON')
517+
if cql_ops:
518+
print("JJ2", json_post_data)
519+
LOGGER.debug('Adding STAC API query parameters to CQL2 JSON')
520+
json_post_data['args'].extend(cql_ops)
521+
459522
headers, status, response = super().items(headers_, json_post_data, args, collection)
460523

461524
response = json.loads(response)

tests/functionaltests/suites/stac_api/test_stac_api_functional.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ def test_items(config):
287287
assert content['numberMatched'] == 1
288288
assert content['features'][0]['properties']['view:off_nadir'] == 3.8
289289

290+
# test post CQL2 JSON requests
290291
cql_json = {
291292
'filter-lang': 'cql2-json',
292293
'filter': {

0 commit comments

Comments
 (0)