Skip to content

Commit

Permalink
Add support for image collection visualizations and animated GIF
Browse files Browse the repository at this point in the history
  • Loading branch information
m-mohr committed May 26, 2024
1 parent 5d5f0ef commit 23bcef3
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 15 deletions.
24 changes: 19 additions & 5 deletions src/formats/bitmap.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ EPSGCODE_PARAMETER_BITMAP.default = 4326;
EPSGCODE_PARAMETER_BITMAP.description += 'Defaults to WGS 84 (EPSG Code 4326).';

const VISUALIZATION_PARAMETER = {
collectionRenderer: {
description: "For image collections (time series) a specific method combine the images into a single image can be chosen.",
type: "string",
enum: ["filmstrip", "mosaic"],
default: "mosaic",
optional: true
},
bands: {
description: "Band selection for visualization",
oneOf: [
Expand Down Expand Up @@ -69,8 +76,8 @@ const VISUALIZATION_PARAMETER = {

export default class BitmapLike extends FileFormat {

constructor(title) {
super(title);
constructor(title, description = '') {
super(title, {}, description);
this.addParameter('epsgCode', EPSGCODE_PARAMETER_BITMAP);
this.addParameter('size', SIZE_PARAMETER);
this.addParameters(VISUALIZATION_PARAMETER);
Expand All @@ -84,7 +91,12 @@ export default class BitmapLike extends FileFormat {
return null;
}

preprocess(node, allowMultiple) {
allowMultiple(parameters) {
const renderer = parameters.collectionRenderer || 'mosaic';
return renderer === 'filmstrip';
}

preprocess(node) {
const ee = node.ee;
const dc = node.getResult();
const parameters = dc.getOutputFormatParameters();
Expand All @@ -111,7 +123,8 @@ export default class BitmapLike extends FileFormat {

const visConfig = {min: 0, max: 255, bands, palette};

let eeData = GeeResults.toImageOrCollection(node, dc.getData(), allowMultiple)
const allowMultiple = this.allowMultiple(parameters);
let eeData = GeeResults.toImageOrCollection(node, dc.getData(), allowMultiple);
if (eeData instanceof ee.ImageCollection) {
eeData = eeData.map(img => img.visualize(visConfig));
}
Expand Down Expand Up @@ -153,8 +166,9 @@ export default class BitmapLike extends FileFormat {
region,
crs
};
const urlFunc = (img instanceof ee.ImageCollection) ? 'getFilmstripThumbURL' : 'getThumbURL';
return await new Promise((resolve, reject) => {
img.getThumbURL(eeOpts, (url, err) => {
img[urlFunc](eeOpts, (url, err) => {
if (err) {
reject(err);
}
Expand Down
14 changes: 12 additions & 2 deletions src/formats/fileformat.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ export const SCALE_PARAMETER = {

export default class FileFormat {

constructor(title, parameters = {}) {
constructor(title, parameters = {}, description = '') {
this.title = title;
this.description = description;
this.parameters = parameters;
}

Expand All @@ -42,6 +43,10 @@ export default class FileFormat {
this.parameters[name] = parameter;
}

removeParameter(name) {
delete this.parameters[name];
}

getFileExtension(/*parameters*/) {
return '';
}
Expand All @@ -56,17 +61,22 @@ export default class FileFormat {
toJSON() {
return {
title: this.title,
description: this.description,
gis_data_types: this.getGisDataTypes(),
parameters: this.parameters
};
}

preprocess(node/*, allowMultiple*/) {
preprocess(node) {
return node.getResult();
}

async retrieve(/*ee, dc*/) {
throw new Error('Not implemented');
}

async export(/*ee, dc*/) {
throw new Error('Not implemented');
}

}
62 changes: 62 additions & 0 deletions src/formats/gif.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import Utils from "../utils/utils.js";
import BitmapLike from "./bitmap.js";

export default class GifFormat extends BitmapLike {
constructor() {
super('GIF (animated)', 'Either animated or static.');
this.removeParameter('collectionRenderer');
this.addParameter('framesPerSecond', {
type: 'number',
description: 'Number of images that are shown per second.',
default: 1,
optional: true,
exclusiveMinimum: 0
});
}

getFileExtension(/*parameters*/) {
return '.gif';
}

getFormatCode() {
return 'gif';
}

allowMultiple() {
return true;
}

async retrieve(ee, dc) {
const parameters = dc.getOutputFormatParameters();
const img = ee.ImageCollection(dc.getData());

let region = null;
let crs = null;
if (dc.hasXY()) {
region = Utils.bboxToGeoJson(dc.getSpatialExtent());
crs = Utils.crsToString(dc.getCrs());
}

const eeOpts = {
format: this.getFormatCode(),
dimensions: parameters.size || 1000,
region,
crs,
framesPerSecond: parameters.framesPerSecond || 1
};
return await new Promise((resolve, reject) => {
img.getVideoThumbURL(eeOpts, (url, err) => {
if (err) {
reject(err);
}
else if (typeof url !== 'string' || url.length === 0) {
reject('Download URL provided by Google Earth Engine is empty.');
}
else {
resolve(url);
}
});
});
}

}
6 changes: 3 additions & 3 deletions src/formats/gtiff.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@ export default class GTiffFormat extends FileFormat {
return parameters.zipped ? '.zip' : '.tiff';
}

preprocess(node, allowMultiple) {
preprocess(node) {
const dc = node.getResult();
const parameters = dc.getOutputFormatParameters();
const dc2 = new DataCube(node.ee, dc);
if (dc2.hasXY() && parameters.epsgCode >= 1000) {
dc2.setCrs(parameters.epsgCode);
}
if (!allowMultiple && dc2.hasT()) {
if (dc2.hasT()) {
dc2.dimT().drop();
}
return dc2.setData(GeeResults.toImageOrCollection(node, dc.getData(), allowMultiple));
return dc2.setData(GeeResults.toImageOrCollection(node, dc.getData()));
}

async retrieve(ee, dc) {
Expand Down
2 changes: 1 addition & 1 deletion src/formats/json.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default class JsonFormat extends FileFormat {
}

getGisDataTypes() {
return ['raster', 'vector', 'table', 'other'];
return ['vector', 'table', 'other'];
}

async retrieve(ee, dc) {
Expand Down
6 changes: 3 additions & 3 deletions src/processes/utils/results.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import HttpUtils from '../../utils/http.js';

const GeeResults = {

toImageOrCollection(node, data, allowMultiple) {
toImageOrCollection(node, data, allowMultiple = false) {
const ee = node.ee;
const eeData = GeeTypes.toEE(node, data);
if (eeData instanceof ee.Image) {
Expand Down Expand Up @@ -42,7 +42,7 @@ const GeeResults = {
},

// Returns AxiosResponse (object) or URL (string)
async retrieve(node, allowMultiple = false) {
async retrieve(node) {
const logger = node.getLogger();
let dc = node.getResult();
const config = node.getServerContext();
Expand All @@ -56,7 +56,7 @@ const GeeResults = {
});
}

dc = format.preprocess(node, allowMultiple);
dc = format.preprocess(node);

let response = await format.retrieve(node.ee, dc);
if (typeof response === 'string') {
Expand Down
4 changes: 3 additions & 1 deletion src/utils/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import GTiffFormat from "../formats/gtiff.js";
import JpegFormat from "../formats/jpeg.js";
import JsonFormat from "../formats/json.js";
import PngFormat from "../formats/png.js";
import GifFormat from "../formats/gif.js";
import Utils from "./utils.js";

export default class Config {
Expand Down Expand Up @@ -40,9 +41,10 @@ export default class Config {

this.inputFormats = {};
this.outputFormats = {
GTIFF: new GTiffFormat(),
PNG: new PngFormat(),
JPEG: new JpegFormat(),
GTIFF: new GTiffFormat(),
GIF: new GifFormat(),
JSON: new JsonFormat()
};

Expand Down

0 comments on commit 23bcef3

Please sign in to comment.