Skip to content

Commit

Permalink
Support for STAC 1.1, datacube subtype and other fixes from dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
m-mohr committed Aug 12, 2024
1 parent 5ecbf8f commit 27f9f2d
Show file tree
Hide file tree
Showing 9 changed files with 47 additions and 41 deletions.
8 changes: 4 additions & 4 deletions docs/geotiff.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ What is required by back-ends to give users an ideal experience with GeoTiff ima
5. The [`PhotometricInterpretation`](https://www.awaresystems.be/imaging/tiff/tifftags/photometricinterpretation.html) of the image should be set to `1` (BlackIsZero) if an RGB interpretation is not clear. If RGB is set as interpretation (`2`) and you have more than 3 samples per pixel ([`SamplesPerPixel`](https://www.awaresystems.be/imaging/tiff/tifftags/samplesperpixel.html)), the [`ExtraSamples`](https://www.awaresystems.be/imaging/tiff/tifftags/extrasamples.html) should be set.
6. [`ColorMap`](https://www.awaresystems.be/imaging/tiff/tifftags/colormap.html)s are supported.
7. For batch jobs, the STAC metadata is recommended to contain per asset:
1. The no-data value either in `file:nodata` (deprecated) or in `nodata` in `raster:bands`
2. The `minimum` and `maximum` values per band in the `statistics` object in `raster:bands`
3. A band `name` either in `raster:bands` (unspecified) or `eo:bands`
4. The projection in `proj:epsg` (recommended), `proj:wkt2` (not well suported by OpenLayers) or `proj:proj4` (deprecated by STAC)
1. The no-data value either in `file:nodata` (deprecated) or in `nodata` in `bands`
2. The `minimum` and `maximum` values per band in the `statistics` object in `bands`
3. A band `name` in `bands`
4. The projection in `proj:code` (recommended), `proj:wkt2` (not well suported by OpenLayers) or `proj:proj4` (deprecated by STAC)
5. The `type` must be set to the corresponding media type (see below)
8. For synchronous execution, the `Content-Type` in the header of the response must be set to the corresponding media type (see below)

Expand Down
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@
"@kirtandesai/ol-geocoder": "^5.0.6",
"@musement/iso-duration": "^1.0.0",
"@openeo/js-client": "^2.6.0",
"@openeo/js-commons": "^1.4.1",
"@openeo/js-processgraphs": "^1.3.0",
"@openeo/vue-components": "^2.16.0",
"@openeo/js-commons": "^1.5.0",
"@openeo/js-processgraphs": "^1.4.1",
"@openeo/vue-components": "^2.17.0",
"@radiantearth/stac-fields": "^1.5.0-beta.2",
"@radiantearth/stac-migrate": "^2.0.0-beta.1",
"@tmcw/togeojson": "^5.5.0",
"ajv": "^6.12.6",
"axios": "^1.0.0",
Expand All @@ -62,7 +64,7 @@
"core-js": "^3.7.0",
"jsonlint-mod": "^1.7.6",
"luxon": "^2.4.0",
"node-polyfill-webpack-plugin": "^2.0.0",
"node-polyfill-webpack-plugin": "^4.0.0",
"ol": "^9.2.0",
"ol-ext": "^4.0.21",
"proj4": "^2.7.5",
Expand Down
5 changes: 5 additions & 0 deletions src/components/JobPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import Utils from '../utils.js';
import { Job } from '@openeo/js-client';
import { cancellableRequest, showCancellableRequestError, CancellableRequestError } from './cancellableRequest';
import FieldMixin from './FieldMixin';
import StacMigrate from '@radiantearth/stac-migrate';
const WorkPanelMixinInstance = WorkPanelMixin('jobs', 'batch job', 'batch jobs');
Expand Down Expand Up @@ -261,6 +262,7 @@ export default {
if (updatedJob.status === 'finished') {
try {
result = await updatedJob.getResultsAsStac();
result = StacMigrate.stac(result, false);
} catch (error) {
Utils.exception(this, error, "Load Results Error: " + Utils.getResourceTitle(updatedJob));
}
Expand Down Expand Up @@ -342,6 +344,7 @@ export default {
// Doesn't need to go through job store as it doesn't change job-related data
try {
let stac = await job.getResultsAsStac();
stac = StacMigrate.stac(stac, false);
this.broadcast('viewJobResults', stac, job);
} catch(error) {
Utils.exception(this, error, 'View Result Error: ' + Utils.getResourceTitle(job));
Expand All @@ -351,6 +354,7 @@ export default {
// Doesn't need to go through job store as it doesn't change job-related data
try {
let result = await job.getResultsAsStac();
result = StacMigrate.stac(result, false);
if(Utils.size(result.assets) == 0) {
Utils.error(this, 'No results available for job "' + Utils.getResourceTitle(job) + '".');
return;
Expand All @@ -363,6 +367,7 @@ export default {
async shareResults(job) {
if (this.canShare) {
let result = await job.getResultsAsStac();
result = StacMigrate.stac(result, false);
let url;
let link;
if (Array.isArray(result.links)) {
Expand Down
2 changes: 1 addition & 1 deletion src/components/datatypes/SelectBox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default {
let collection = this.$store.state.collections.find(c => c.id == this.context);
if (Utils.isObject(collection)) {
try {
state = collection.summaries['eo:bands'].map(band => band.name);
state = collection.summaries['bands'].map(band => band.name);
} catch (error) {}
if (state.length === 0 && Utils.isObject(collection['cube:dimensions'])) {
try {
Expand Down
10 changes: 5 additions & 5 deletions src/components/maps/projManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ export default class ProjManager {
// Get projection details from STAC (todo: add collection support)
static async addFromStac(stac) {
if (Utils.isObject(stac) && Utils.isObject(stac.properties)) {
if (stac.properties['proj:epsg']) {
return await ProjManager.get(stac.properties['proj:epsg']);
if (stac.properties['proj:code']) {
return await ProjManager.get(stac.properties['proj:code']);
}
else if (stac.properties['proj:wkt2']) {
return ProjManager.add(stac.id, stac.properties['proj:wkt2']);
Expand Down Expand Up @@ -68,9 +68,9 @@ export default class ProjManager {
}

// Get projection from database
let proj = await import('../../assets/epsg-proj.json');
if (id in proj) {
return ProjManager.add(code, proj[id][0], proj[id][1]);
let epsg = await import('../../assets/epsg-proj.json');
if (id in epsg) {
return ProjManager.add(code, epsg[id][0], epsg[id][1]);
}

// No projection found
Expand Down
3 changes: 2 additions & 1 deletion src/components/modals/CollectionModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import Modal from './Modal.vue';
import Collection from '../Collection.vue';
import Utils from '../../utils.js';
import StacMigrate from '@radiantearth/stac-migrate';
export default {
name: 'CollectionModal',
Expand Down Expand Up @@ -82,7 +83,7 @@ export default {
}
let next = await this.itemsIterator.next();
if (next && next.value && !next.done) {
this.items.push(next.value);
this.items.push(StacMigrate.item(next.value, null, false));
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/components/wizards/SpectralIndices.vue
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export default {
return false;
}
if (c.summaries && !c.summaries["eo:bands"]) {
if (c.summaries && !c.summaries["bands"]) {
// Has summaries (so is likely fully loaded), but has no bands that we can work with
return false;
}
Expand Down Expand Up @@ -196,7 +196,7 @@ export default {
return b.toJSON();
},
getAvailableBands(collection) {
let bands = collection?.summaries && collection?.summaries["eo:bands"];
let bands = collection?.summaries && collection?.summaries["bands"];
if (Array.isArray(bands)) {
let availableBands = {};
const stacNames = Object.values(MAPPING);
Expand All @@ -206,7 +206,7 @@ export default {
if (!band.name) {
continue; // Ignore bands without a name
}
let i = stacNames.indexOf(band['common_name']);
let i = stacNames.indexOf(band['eo:common_name']);
if (i !== -1) {
availableBands[asiNames[i]] = band;
}
Expand Down
41 changes: 18 additions & 23 deletions src/formats/geotiff.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ class GeoTIFF extends SupportedFormat {

constructor(asset, stac) {
super(asset, "MapViewer", 'fa-map', { removableLayers: true });
this.bands = [];
this.nodata = [];
this._bands = [];
this._nodata = [];
this.img = null;
this.projection = null;
this.extent = null;
Expand Down Expand Up @@ -53,17 +53,12 @@ class GeoTIFF extends SupportedFormat {

// Get nodata from STAC file:nodata
if (Array.isArray(this['file:nodata']) && this['file:nodata'].length > 0) {
this.nodata = Utils.parseNodata(this['file:nodata']);
this._nodata = Utils.parseNodata(this['file:nodata']);
}

// Get band names from STAC eo:bands
if (Array.isArray(this['eo:bands']) && this['eo:bands'].length > 0) {
this['eo:bands'].forEach((band, i) => this.setBandInfo(i, { name: band.name }));
}

// Get min/max/nodata from STAC raster:bands
if (Array.isArray(this['raster:bands']) && this['raster:bands'].length > 0) {
this['raster:bands'].forEach((band, i) => {
// Get min/max/nodata from STAC bands
if (Array.isArray(this.bands) && this.bands.length > 0) {
this.bands.forEach((band, i) => {
// Get name from band
if (band.name) {
this.setBandInfo(i, {
Expand All @@ -80,8 +75,8 @@ class GeoTIFF extends SupportedFormat {
}

// per-band no-data values are not supported, simply read the no-data from the first occurance if not defined yet
if (this.nodata.length === 0 && typeof band.nodata !== 'undefined') {
this.nodata.push(Utils.parseNodata(band.nodata));
if (this._nodata.length === 0 && typeof band.nodata !== 'undefined') {
this._nodata.push(Utils.parseNodata(band.nodata));
}
});
}
Expand All @@ -103,10 +98,10 @@ class GeoTIFF extends SupportedFormat {
// Use min/max for data type (as fallback)
try {
let dummy = this.img.getArrayForSample(i);
if (!Number.isFinite(this.bands[i].min)) {
if (!Number.isFinite(this._bands[i].min)) {
data.min = this.getMinForDataType(dummy);
}
if (!Number.isFinite(this.bands[i].max)) {
if (!Number.isFinite(this._bands[i].max)) {
data.max = this.getMaxForDataType(dummy);
}
} catch (error) {}
Expand All @@ -129,8 +124,8 @@ class GeoTIFF extends SupportedFormat {

// get no-data values if needed
let nodata = this.img.getGDALNoData();
if (this.nodata.length === 0 && nodata !== null) {
this.nodata.push(nodata);
if (this._nodata.length === 0 && nodata !== null) {
this._nodata.push(nodata);
}
}

Expand Down Expand Up @@ -176,7 +171,7 @@ class GeoTIFF extends SupportedFormat {
Math.trunc(map[i] / 65536 * 256),
Math.trunc(map[i + greenOffset] / 65536 * 256),
Math.trunc(map[i + blueOffset] / 65536 * 256),
this.nodata.includes(i) ? 0 : 1
this._nodata.includes(i) ? 0 : 1
]);
}
}
Expand All @@ -196,11 +191,11 @@ class GeoTIFF extends SupportedFormat {
}

setBandInfo(i, data) {
if (this.bands[i]) {
Object.assign(this.bands[i], data);
if (this._bands[i]) {
Object.assign(this._bands[i], data);
}
else {
this.bands.push(Object.assign({ id: i + 1 }, data));
this._bands.push(Object.assign({ id: i + 1 }, data));
}
}

Expand All @@ -209,15 +204,15 @@ class GeoTIFF extends SupportedFormat {
}

getNoData() {
return this.nodata;
return this._nodata;
}

getContext() {
return this.stac;
}

getBands() {
return this.bands;
return this._bands;
}

getProjection() {
Expand Down
3 changes: 3 additions & 0 deletions src/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Vuex from 'vuex';

import { OpenEO, FileTypes, Formula } from '@openeo/js-client';
import { ProcessRegistry } from '@openeo/js-commons';
import StacMigrate from '@radiantearth/stac-migrate';
import Utils from '../utils.js';
import ProcessRegistryExtension from '../registryExtension.js';
import Config from '../../config';
Expand Down Expand Up @@ -272,6 +273,7 @@ export default new Vuex.Store({
let collection = cx.state.collections.find(c => c.id === id);
if (!collection || !collection._loaded) {
collection = await cx.state.connection.describeCollection(id);
collection = StacMigrate.collection(collection, false);
cx.commit('fillCollection', collection);
}
return collection;
Expand Down Expand Up @@ -384,6 +386,7 @@ export default new Vuex.Store({
},
collections(state, data) {
state.collections = data.collections
.map(c => StacMigrate.collection(c, false))
.filter(c => (typeof c.id === 'string'))
.sort(Utils.sortById);
},
Expand Down

0 comments on commit 27f9f2d

Please sign in to comment.