Skip to content

Commit

Permalink
Implement MVP for OGC API - Coverages
Browse files Browse the repository at this point in the history
  • Loading branch information
m-mohr committed Oct 17, 2024
1 parent ed2586f commit 875823e
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 11 deletions.
24 changes: 24 additions & 0 deletions src/api/capabilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,33 @@ export default class CapabilitiesAPI {
type: 'application/json',
title: 'Supported API versions'
},
// STAC
{
rel: "data",
href: API.getUrl("/collections"),
type: "application/json",
title: "Datasets"
},
// OGC API - Coverages
{
rel: "http://www.opengis.net/def/rel/ogc/1.0/data",
href: API.getUrl("/collections"),
type: "application/json",
title: "Datasets"
},
// STAC and older OGC APIs
{
rel: "conformance",
href: API.getUrl("/conformance"),
type: "application/json",
title: "OGC Conformance classes"
},
// Some newer OGC APIs (including Coverages)
{
rel: "http://www.opengis.net/def/rel/ogc/1.0/conformance",
href: API.getUrl("/conformance"),
type: "application/json",
title: "OGC Conformance classes"
}
]
});
Expand Down Expand Up @@ -150,6 +166,14 @@ export default class CapabilitiesAPI {
// CQL2 (for Item and Collection Filter)
// "http://www.opengis.net/spec/cql2/1.0/conf/cql2-text",
// "http://www.opengis.net/spec/cql2/1.0/conf/basic-cql2",
// OGC API - Coverages / GDC API
"http://www.opengis.net/spec/ogcapi-coverages-1/1.0/conf/core",
"http://www.opengis.net/spec/ogcapi-coverages-1/1.0/conf/geotiff",
"http://www.opengis.net/spec/ogcapi-coverages-1/1.0/conf/png",
// "http://www.opengis.net/spec/ogcapi-coverages-1/1.0/conf/crs",
// "http://www.opengis.net/spec/ogcapi-coverages-1/1.0/conf/scaling",
// "http://www.opengis.net/spec/ogcapi-coverages-1/1.0/conf/subsetting",
// "http://www.opengis.net/spec/ogcapi-coverages-1/1.0/conf/fieldselection",
]
});
}
Expand Down
61 changes: 51 additions & 10 deletions src/api/collections.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Utils from '../utils/utils.js';
import Errors from '../utils/errors.js';
import GeeProcessing from '../processes/utils/processing.js';
import HttpUtils from '../utils/http.js';
import runSync from './worker/sync.js';

const sortPropertyMap = {
'properties.datetime': 'system:time_start',
Expand Down Expand Up @@ -36,6 +37,8 @@ export default class Data {
server.addEndpoint('get', ['/collections/{collection_id}', '/collections/*'], this.getCollectionById.bind(this));
server.addEndpoint('get', '/collections/{collection_id}/queryables', this.getCollectionQueryables.bind(this));
server.addEndpoint('get', '/collections/{collection_id}/items', this.getCollectionItems.bind(this));
server.addEndpoint('get', '/collections/{collection_id}/schema', this.getCollectionSchema.bind(this));
server.addEndpoint('get', '/collections/{collection_id}/coverage', this.getCoverage.bind(this));
server.addEndpoint('get', '/collections/{collection_id}/items/{item_id}', this.getCollectionItemById.bind(this));
if (this.context.stacAssetDownloadSize > 0) {
server.addEndpoint('get', ['/assets/{asset_id}', '/assets/*'], this.getAssetById.bind(this));
Expand Down Expand Up @@ -106,6 +109,12 @@ export default class Data {
else if (id.endsWith('/queryables')) {
return await this.getCollectionQueryables(req, res);
}
else if (id.endsWith('/schema')) {
return await this.getCollectionSchema(req, res);
}
else if (id.endsWith('/coverage')) {
return await this.getCoverage(req, res);
}
else if (id.endsWith('/items')) {
return await this.getCollectionItems(req, res);
}
Expand All @@ -121,28 +130,60 @@ export default class Data {
res.json(collection);
}

async getCollectionQueryables(req, res) {
getCollectionId(req, endpoint) {
let id = req.params.collection_id;
// Get the ID if this was a redirect from the /collections/{collection_id} endpoint
// Get the ID if this was a redirect from another endpoint
if (req.params['*'] && !id) {
id = req.params['*'].replace(/\/queryables$/, '');
endpoint = '/' + endpoint;
id = req.params['*'];
if (id.endsWith(endpoint)) {
id = id.substring(0, req.params['*'].length - endpoint.length);
}
}
return id;
}

const queryables = this.catalog.getSchema(id);
async getCollectionQueryables(req, res) {
const id = this.getCollectionId(req, 'queryables');
const queryables = this.catalog.getQueryables(id);
if (queryables === null) {
throw new Errors.CollectionNotFound();
}

res.json(queryables);
}

async getCollectionItems(req, res) {
let id = req.params.collection_id;
// Get the ID if this was a redirect from the /collections/{collection_id} endpoint
if (req.params['*'] && !id) {
id = req.params['*'].replace(/\/items$/, '');
async getCollectionSchema(req, res) {
const id = this.getCollectionId(req, 'schema');
const schema = this.catalog.getSchema(id);
if (schema === null) {
throw new Errors.CollectionNotFound();
}
res.json(schema);
}

async getCoverage(req, res) {
if (!req.user._id) {
throw new Errors.AuthenticationRequired();
}

const id = this.getCollectionId(req, 'coverage');
const collection = this.catalog.getData(id);
if (collection === null) {
throw new Errors.CollectionNotFound();
}

// const schema = this.catalog.getSchema(id);

const process = {};

const response = await runSync(this.context, req.user, Utils.timeId(), process, "error");

res.header('Content-Type', response?.headers?.['content-type'] || 'application/octet-stream');
response.data.pipe(res);
}

async getCollectionItems(req, res) {
const id = this.getCollectionId(req, 'items');
const collection = this.catalog.getData(id, true);
if (collection === null) {
throw new Errors.CollectionNotFound();
Expand Down
49 changes: 48 additions & 1 deletion src/models/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export default class DataCatalog {
return await this.readLocalCatalog();
}

getSchema(id) {
getQueryables(id) {
const collection = this.getData(id, true);
if (!collection) {
return null;
Expand Down Expand Up @@ -211,6 +211,31 @@ export default class DataCatalog {
return jsonSchema;
}

getSchema(id) {
const collection = this.getData(id, true);
if (!collection) {
return null;
}

const jsonSchema = {
"$schema" : "https://json-schema.org/draft/2019-09/schema",
"$id" : API.getUrl(`/collections/${id}/schema`),
"title" : "Schema",
"type" : "object",
"properties" : {
"var": {
"title": "",
"type": "number",
"description": "",
"x-ogc-propertySeq": 0
}
},
"additionalProperties": false
};

return jsonSchema;
}

getData(id = null, withSchema = false) {
if (id !== null) {
if (typeof this.collections[id] !== 'undefined') {
Expand Down Expand Up @@ -257,12 +282,25 @@ export default class DataCatalog {
}
return l;
});
// STAC etc.
c.links.push({
rel: 'http://www.opengis.net/def/rel/ogc/1.0/queryables',
href: API.getUrl(`/collections/${c.id}/queryables`),
title: "Queryables",
type: "application/schema+json"
});
// OGC API - Coverages
c.links.push({
rel: "http://www.opengis.net/def/rel/ogc/1.0/schema",
href: API.getUrl(`/collections/${c.id}/schema`),
type: "application/schema+json"
});
// OGC API - Coverages
c.links.push({
rel: "http://www.opengis.net/def/rel/ogc/1.0/coverage",
href: API.getUrl(`/collections/${c.id}/schema`),
type: "application/schema+json"
});
if (c["gee:type"] === 'image_collection') {
c.links.push({
rel: 'items',
Expand Down Expand Up @@ -538,6 +576,11 @@ export default class DataCatalog {
console.log("Invalid spatial extent for " + c.id);
}
else {
// OGC API - Coverages
c.extent.spatial.grid = [{},{}];
// c.extent.sparial.storageCrsBbox = [];
c.extent.temporal.grid = {};

// spatial dimensions for all data types
const x2 = c.extent.spatial.bbox[0].length > 4 ? 3 : 2;
const y2 = c.extent.spatial.bbox[0].length > 4 ? 4 : 3;
Expand Down Expand Up @@ -578,6 +621,10 @@ export default class DataCatalog {
// Unfortunately, no other information available
c['cube:dimensions'].x.reference_system = c.summaries['proj:epsg'][0];
c['cube:dimensions'].y.reference_system = c.summaries['proj:epsg'][0];

// OGC API - Coverages
c.storageCrs = `http://www.opengis.net/def/crs/EPSG/0/${c.summaries['proj:epsg'][0]}`;
//c.crs = [];
}

if (!Utils.isObject(c.assets)) {
Expand Down

0 comments on commit 875823e

Please sign in to comment.