Skip to content

Commit

Permalink
made compliancy checks succeed
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasstorm committed Nov 10, 2023
1 parent 8e99396 commit ab10091
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 52 deletions.
133 changes: 106 additions & 27 deletions xcube_geodb_openeo/api/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
# DEALINGS IN THE SOFTWARE.
import datetime
import importlib
import re
from functools import cached_property
from typing import Any, List
from typing import Dict
Expand All @@ -36,8 +37,7 @@
from ..core.vectorcube import VectorCube
from ..core.vectorcube_provider import VectorCubeProvider
from ..defaults import default_config, STAC_VERSION, STAC_EXTENSIONS, \
STAC_MAX_ITEMS_LIMIT, DEFAULT_VC_CACHE_SIZE, \
MAX_NUMBER_OF_GEOMETRIES_DISPLAYED
DEFAULT_VC_CACHE_SIZE


class GeoDbContext(ApiContext):
Expand Down Expand Up @@ -151,28 +151,45 @@ def get_collection_items(
offset: int, bbox: Optional[Tuple[float, float, float, float]]
= None) -> Dict:
vector_cube = self.get_vector_cube(collection_id, bbox=bbox)
stac_features = [
_get_vector_cube_item(base_url, vector_cube, feature)
for feature in vector_cube.load_features(limit, offset)
]

result = {
'type': 'FeatureCollection',
'features': stac_features,
'timeStamp': _utc_now(),
'numberMatched': vector_cube.feature_count,
'numberReturned': len(stac_features)
}
stac_features = []
for feature in vector_cube.load_features(limit, offset):
_fix_time(feature)
stac_features.append(
_get_vector_cube_item(base_url, vector_cube, feature)
)

result = {'type': 'FeatureCollection', 'features': stac_features,
'timeStamp': _utc_now(),
'numberMatched': vector_cube.feature_count,
'numberReturned': len(stac_features),
'links': [
{
'rel': 'self',
'href': f'{base_url}/collections/'
f'{vector_cube.id}/items',
'type': 'application/json'
},
{
'rel': 'root',
'href': f'{base_url}',
'type': 'application/json'
},
{
'rel': 'items',
'href': f'{base_url}collections/'
f'{vector_cube.id}/items',
'type': 'application/json'
}]
}

if offset + limit < vector_cube.feature_count:
new_offset = offset + limit
result['links'] = [
result['links'].append(
{
'rel': 'next',
'href': f'{base_url}/collections/{vector_cube.id}'
f'/items?limit={limit}&offset={new_offset}'
},
]
})

return result

Expand All @@ -197,6 +214,13 @@ def transform_bbox(self, collection_id: Tuple[str, str],
def get_collections_links(limit: int, offset: int, url: str,
collection_count: int):
links = []
root_url = url.replace('/collections', '')
root_link = {'rel': 'root',
'href': f'{root_url}',
'title': 'root'}
self_link = {'rel': 'self',
'href': f'{url}',
'title': 'self'}
next_offset = offset + limit
next_link = {'rel': 'next',
'href': f'{url}?limit={limit}&offset='f'{next_offset}',
Expand All @@ -213,6 +237,8 @@ def get_collections_links(limit: int, offset: int, url: str,
'href': f'{url}?limit={limit}&offset='f'{last_offset}',
'title': 'last'}

links.append(root_link)
links.append(self_link)
if next_offset < collection_count:
links.append(next_link)
if offset > 0:
Expand Down Expand Up @@ -244,11 +270,23 @@ def _get_vector_cube_collection(base_url: str,
'links': [
{
'rel': 'self',
'href': f'{base_url}/collections/{vector_cube_id}'
'href': f'{base_url}/collections/{vector_cube_id}',
"type": "application/json"
},
{
'rel': 'root',
'href': f'{base_url}/collections/'
'href': f'{base_url}',
"type": "application/json"
},
{
'rel': 'parent',
'href': f'{base_url}/collections/',
"type": "application/json"
},
{
'rel': 'items',
'href': f'{base_url}/collections/{vector_cube_id}/items',
"type": "application/json"
}
]
}
Expand All @@ -267,8 +305,9 @@ def _get_vector_cube_collection(base_url: str,
'reference_system': srid
}
}
vector_cube_collection['summaries'] = metadata.get('summaries', {}),

vector_cube_collection['summaries'] = (metadata['summaries']
if 'summaries' in metadata
else {})
if 'version' in metadata:
vector_cube_collection['version'] = metadata['version']
return vector_cube_collection
Expand All @@ -281,12 +320,11 @@ def _get_vector_cube_item(base_url: str, vector_cube: VectorCube,
feature_bbox = feature.get('bbox')
feature_geometry = feature.get('geometry')
feature_properties = feature.get('properties', {})
feature_datetime = feature.get('datetime') \
if 'datetime' in feature else None

item = {
'stac_version': STAC_VERSION,
'stac_extensions': STAC_EXTENSIONS,
'stac_extensions': ['https://schemas.stacspec.org/v1.0.0/item-spec/'
'json-schema/item.json'],
'type': 'Feature',
'id': feature_id,
'bbox': feature_bbox,
Expand All @@ -298,12 +336,25 @@ def _get_vector_cube_item(base_url: str, vector_cube: VectorCube,
'rel': 'self',
'href': f'{base_url}/collections/'
f'{collection_id}/items/{feature_id}'
}
},
{
'rel': 'root',
'href': f'{base_url}',
'type': 'application/json'
},
{
'rel': 'parent',
'href': f'{base_url}/collections',
'type': 'application/json'
},
{
'rel': 'collection',
'href': f'{base_url}/collections/{collection_id}',
'type': 'application/json'
}
],
'assets': {}
}
if feature_datetime:
item['datetime'] = feature_datetime
return item


Expand All @@ -314,6 +365,34 @@ def _utc_now():
.replace(microsecond=0) \
.isoformat() + 'Z'


def _fix_time(feature):
time_column = _get_col_name(feature, ['date', 'time', 'timestamp',
'datetime'])
props = feature['properties']
if time_column and time_column != 'datetime':
props['datetime'] = props[time_column]
del props[time_column]
if 'datetime' not in props:
props['datetime'] = (datetime.datetime
.strptime('19700101', '%Y%M%d')
.isoformat() + 'Z')
if re.match('^\d\d\d\d.\d\d.\d\d$', props['datetime']):
props['datetime'] = props['datetime'] + 'T00:00:00Z'
is_tz_aware = (props['datetime'].endswith('Z')
or props['datetime'].endswith('+00:00'))
if props['datetime'] and not is_tz_aware:
props['datetime'] = props['datetime'] + 'Z'


def _get_col_name(feature: Feature, possible_names: List[str]) \
-> Optional[str]:
for key in feature['properties'].keys():
if key in possible_names:
return key
return None


class CollectionNotFoundException(Exception):
pass

Expand Down
45 changes: 38 additions & 7 deletions xcube_geodb_openeo/api/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@
import requests

from openeo.internal.graph_building import PGNode
from xcube.constants import LOG
from xcube.server.api import ApiError
from xcube.server.api import ApiHandler
from xcube_geodb.core.geodb import GeoDBError

from .api import api
from .context import _fix_time
from ..backend import capabilities
from ..backend import processes
from ..defaults import STAC_DEFAULT_ITEMS_LIMIT, STAC_MAX_ITEMS_LIMIT, \
Expand Down Expand Up @@ -260,6 +263,7 @@ def get(self):


@api.route('/collections/{collection_id}')
@api.route('/collections/{collection_id}/')
class CollectionHandler(ApiHandler):
"""
Lists all information about a specific collection specified by the
Expand All @@ -271,14 +275,20 @@ def get(self, collection_id: str):
Lists the collection information.
"""
base_url = get_base_url(self.request)
if '~' not in collection_id:
self.response.set_status(404,
f'Collection {collection_id} does '
f'not exist')
return
db = collection_id.split('~')[0]
name = collection_id.split('~')[1]
collection = self.ctx.get_collection(base_url, (db, name), True)
if collection:
self.response.finish(collection)
else:
self.response.set_status(404, f'Collection {collection_id} does '
f'not exist')
self.response.set_status(404,
f'Collection {collection_id} does '
f'not exist')


@api.route('/collections/{collection_id}/items')
Expand Down Expand Up @@ -310,7 +320,7 @@ def get(self, collection_id: str):
name = collection_id.split('~')[1]
items = self.ctx.get_collection_items(base_url, (db, name),
limit, offset, bbox)
self.response.finish(items)
self.response.finish(items, content_type='application/geo+json')


@api.route('/collections/{collection_id}/items/{item_id}')
Expand All @@ -327,9 +337,30 @@ def get(self, collection_id: str, item_id: str):
db = collection_id.split('~')[0]
name = collection_id.split('~')[1]
base_url = get_base_url(self.request)
feature = self.ctx.get_collection_item(base_url, (db, name),
feature_id)
self.response.finish(feature)
try:
feature = self.ctx.get_collection_item(base_url, (db, name),
feature_id)
except GeoDBError as e:
if 'does not exist' in e.args[0]:
LOG.warning(f'Not existing feature with id {feature_id} '
f'requested.')
self.response.set_status(404,
f'Feature {feature_id} does '
f'not exist')
return

_fix_time(feature)
self.response.finish(feature, content_type='application/geo+json')


@api.route('/api.html')
class FeatureHandler(ApiHandler):
"""
Simply forwards to openapi.html
"""

def get(self):
self.response._handler.redirect('/openapi.html', status=301)


def _get_limit(request, default=sys.maxsize) -> int:
Expand All @@ -350,4 +381,4 @@ def _get_bbox(request):
bbox = str(request.get_query_arg('bbox'))
return tuple(bbox.split(','))
else:
return None
return None
14 changes: 12 additions & 2 deletions xcube_geodb_openeo/backend/capabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,14 @@ def get_root(config: Mapping[str, Any], base_url: str):
'api_version': API_VERSION,
'backend_version': __version__,
'stac_version': STAC_VERSION,
'type': 'catalog',
'type': 'Catalog',
"id": config['geodb_openeo']['SERVER_ID'],
"title": config['geodb_openeo']['SERVER_TITLE'],
"description": config['geodb_openeo']['SERVER_DESCRIPTION'],
"conformsTo": [
f'https://api.stacspec.org/v1.0.0/{part}'
for part in ['core', 'collections', 'ogcapi-features']
],
'endpoints': [
{'path': '/.well-known/openeo', 'methods': ['GET']},
{'path': '/file_formats', 'methods': ['GET']},
Expand All @@ -52,6 +56,12 @@ def get_root(config: Mapping[str, Any], base_url: str):
'methods': ['GET']},
],
"links": [
{
"rel": "root",
"href": f"{base_url}/",
"type": "application/json",
"title": "this document"
},
{
"rel": "self",
"href": f"{base_url}/",
Expand All @@ -60,7 +70,7 @@ def get_root(config: Mapping[str, Any], base_url: str):
},
{
"rel": "service-desc",
"href": f"{base_url}/api",
"href": f'{base_url}/openapi.json',
"type": "application/vnd.oai.openapi+json;version=3.0",
"title": "the API definition"
},
Expand Down
Loading

0 comments on commit ab10091

Please sign in to comment.