Skip to content

Commit

Permalink
Merge pull request #77 from sentinel-hub/feature/byoc-multi-location
Browse files Browse the repository at this point in the history
Feature/byoc multi location
  • Loading branch information
sinergise-anze authored Apr 29, 2020
2 parents c62790c + a984f4d commit 5da276b
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 41 deletions.
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
parseLegacyWmsGetMapParams,
} from 'src/legacyCompat';
import { AcquisitionMode, Polarization, Resolution } from 'src/layer/S1GRDAWSEULayer';
import { LocationIdSHv3 } from 'src/layer/const';
import { registerAxiosCacheRetryInterceptors } from 'src/utils/axiosInterceptors';

registerAxiosCacheRetryInterceptors();
Expand Down Expand Up @@ -104,6 +105,7 @@ export {
PreviewMode,
S3SLSTRView,
BBox,
LocationIdSHv3,
setDebugEnabled,
// legacy:
legacyGetMapFromUrl,
Expand Down
37 changes: 28 additions & 9 deletions src/layer/AbstractSentinelHubV3Layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ export class AbstractSentinelHubV3Layer extends AbstractLayer {
return payload;
}

protected getShServiceHostname(): string {
return this.dataset.shServiceHostname;
}

public async getMap(params: GetMapParams, api: ApiType): Promise<Blob> {
// SHv3 services support Processing API:
if (api === ApiType.PROCESSING) {
Expand Down Expand Up @@ -135,8 +139,8 @@ export class AbstractSentinelHubV3Layer extends AbstractLayer {
const payload = createProcessingPayload(this.dataset, params, this.evalscript, this.dataProduct);
// allow subclasses to update payload with their own parameters:
const updatedPayload = await this.updateProcessingGetMapPayload(payload);

return processingGetMap(this.dataset.shServiceHostname, updatedPayload);
const shServiceHostname = this.getShServiceHostname();
return processingGetMap(shServiceHostname, updatedPayload);
}

return super.getMap(params, api);
Expand All @@ -157,7 +161,8 @@ export class AbstractSentinelHubV3Layer extends AbstractLayer {
if (!this.dataset) {
throw new Error('This layer does not have a dataset specified');
}
const baseUrl = `${this.dataset.shServiceHostname}ogc/wms/${this.instanceId}`;
const shServiceHostname = this.getShServiceHostname();
const baseUrl = `${shServiceHostname}ogc/wms/${this.instanceId}`;
const evalsource = this.dataset.shWmsEvalsource;
return wmsGetMapUrl(
baseUrl,
Expand Down Expand Up @@ -192,7 +197,14 @@ export class AbstractSentinelHubV3Layer extends AbstractLayer {
maxCount?: number,
offset?: number,
): Promise<PaginatedTiles> {
const response = await this.fetchTiles(bbox, fromTime, toTime, maxCount, offset);
const response = await this.fetchTiles(
this.dataset.searchIndexUrl,
bbox,
fromTime,
toTime,
maxCount,
offset,
);
return {
tiles: response.data.tiles.map(tile => ({
geometry: tile.dataGeometry,
Expand All @@ -204,6 +216,7 @@ export class AbstractSentinelHubV3Layer extends AbstractLayer {
}

protected fetchTiles(
searchIndexUrl: string,
bbox: BBox,
fromTime: Date,
toTime: Date,
Expand All @@ -212,7 +225,7 @@ export class AbstractSentinelHubV3Layer extends AbstractLayer {
maxCloudCoverPercent?: number | null,
datasetParameters?: Record<string, any> | null,
): Promise<{ data: { tiles: any[]; hasMore: boolean } }> {
if (!this.dataset.searchIndexUrl) {
if (!searchIndexUrl) {
throw new Error('This dataset does not support searching for tiles');
}
const bboxPolygon = bbox.toGeoJSON();
Expand All @@ -231,7 +244,7 @@ export class AbstractSentinelHubV3Layer extends AbstractLayer {
payload.datasetParameters = datasetParameters;
}

return axios.post(this.dataset.searchIndexUrl, payload, this.createSearchIndexRequestConfig());
return axios.post(searchIndexUrl, payload, this.createSearchIndexRequestConfig());
}

protected async getFindDatesUTCAdditionalParameters(): Promise<Record<string, any>> {
Expand All @@ -242,8 +255,13 @@ export class AbstractSentinelHubV3Layer extends AbstractLayer {
return {};
}

protected async getFindDatesUTCUrl(): Promise<string> {
return this.dataset.findDatesUTCUrl;
}

public async findDatesUTC(bbox: BBox, fromTime: Date, toTime: Date): Promise<Date[]> {
if (!this.dataset.findDatesUTCUrl) {
const findDatesUTCUrl = await this.getFindDatesUTCUrl();
if (!findDatesUTCUrl) {
throw new Error('This dataset does not support searching for dates');
}

Expand All @@ -254,7 +272,7 @@ export class AbstractSentinelHubV3Layer extends AbstractLayer {
to: toTime.toISOString(),
...(await this.getFindDatesUTCAdditionalParameters()),
};
const response = await axios.post(this.dataset.findDatesUTCUrl, payload);
const response = await axios.post(findDatesUTCUrl, payload);
const found: Moment[] = response.data.map((date: string) => moment.utc(date));

// S-5P, S-3 and possibly other datasets return the results in reverse order (leastRecent).
Expand Down Expand Up @@ -305,7 +323,8 @@ export class AbstractSentinelHubV3Layer extends AbstractLayer {
}
}

const { data } = await axios.post(this.dataset.shServiceHostname + 'ogc/fis/' + this.instanceId, payload);
const shServiceHostname = this.getShServiceHostname();
const { data } = await axios.post(shServiceHostname + 'ogc/fis/' + this.instanceId, payload);
// convert date strings to Date objects
for (let channel in data) {
data[channel] = data[channel].map((dailyStats: any) => ({
Expand Down
1 change: 1 addition & 0 deletions src/layer/AbstractSentinelHubV3WithCCLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export class AbstractSentinelHubV3WithCCLayer extends AbstractSentinelHubV3Layer
offset?: number,
): Promise<PaginatedTiles> {
const response = await this.fetchTiles(
this.dataset.searchIndexUrl,
bbox,
fromTime,
toTime,
Expand Down
82 changes: 60 additions & 22 deletions src/layer/BYOCLayer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import { AxiosRequestConfig } from 'axios';
import moment from 'moment';
import axios from 'axios';

import { getAuthToken } from 'src/auth';
import { BBox } from 'src/bbox';
import { PaginatedTiles } from 'src/layer/const';
import {
PaginatedTiles,
LocationIdSHv3,
SHV3_LOCATIONS_ROOT_URL,
GetMapParams,
ApiType,
} from 'src/layer/const';
import { DATASET_BYOC } from 'src/layer/dataset';
import { AbstractSentinelHubV3Layer } from 'src/layer/AbstractSentinelHubV3Layer';
import { ProcessingPayload } from 'src/layer/processing';
Expand All @@ -16,6 +24,7 @@ interface ConstructorParameters {
title?: string | null;
description?: string | null;
collectionId?: string | null;
locationId?: LocationIdSHv3 | null;
}

type BYOCFindTilesDatasetParameters = {
Expand All @@ -26,6 +35,7 @@ type BYOCFindTilesDatasetParameters = {
export class BYOCLayer extends AbstractSentinelHubV3Layer {
public readonly dataset = DATASET_BYOC;
protected collectionId: string;
protected locationId: LocationIdSHv3;

public constructor({
instanceId = null,
Expand All @@ -36,28 +46,44 @@ export class BYOCLayer extends AbstractSentinelHubV3Layer {
title = null,
description = null,
collectionId = null,
locationId = null,
}: ConstructorParameters) {
super({ instanceId, layerId, evalscript, evalscriptUrl, dataProduct, title, description });
this.collectionId = collectionId;
this.locationId = locationId;
}

private shouldFetchAdditionalParams(): boolean {
if (this.collectionId === null) {
if (this.instanceId === null || this.layerId === null) {
throw new Error(
"Parameter collectionId is not set and can't be fetched from service because instanceId and layerId are not available",
);
}
return true;
public async updateLayerFromServiceIfNeeded(): Promise<void> {
if (this.collectionId !== null && this.locationId !== null) {
return;
}
return false;
}

protected async updateProcessingGetMapPayload(payload: ProcessingPayload): Promise<ProcessingPayload> {
if (this.shouldFetchAdditionalParams()) {
if (this.instanceId === null || this.layerId === null) {
throw new Error(
"Some of layer parameters (collectionId, locationId) are not set and can't be fetched from service because instanceId and layerId are not available",
);
}

if (this.collectionId === null) {
const layerParams = await this.fetchLayerParamsFromSHServiceV3();
this.collectionId = layerParams['collectionId'];
}

if (this.locationId === null) {
const url = `https://services.sentinel-hub.com/api/v1/metadata/collection/CUSTOM/${this.collectionId}`;
const headers = { Authorization: `Bearer ${getAuthToken()}` };
const res = await axios.get(url, { responseType: 'json', headers: headers, useCache: false });
this.locationId = res.data.location.id;
}
}

public async getMap(params: GetMapParams, api: ApiType): Promise<Blob> {
await this.updateLayerFromServiceIfNeeded();
return await super.getMap(params, api);
}

protected async updateProcessingGetMapPayload(payload: ProcessingPayload): Promise<ProcessingPayload> {
await this.updateLayerFromServiceIfNeeded();
payload.input.data[0].dataFilter.collectionId = this.collectionId;
return payload;
}
Expand All @@ -69,16 +95,17 @@ export class BYOCLayer extends AbstractSentinelHubV3Layer {
maxCount?: number,
offset?: number,
): Promise<PaginatedTiles> {
if (this.shouldFetchAdditionalParams()) {
const layerParams = await this.fetchLayerParamsFromSHServiceV3();
this.collectionId = layerParams['collectionId'];
}
await this.updateLayerFromServiceIfNeeded();

const findTilesDatasetParameters: BYOCFindTilesDatasetParameters = {
type: 'BYOC',
collectionId: this.collectionId,
};
// searchIndex URL depends on the locationId:
const rootUrl = SHV3_LOCATIONS_ROOT_URL[this.locationId];
const searchIndexUrl = `${rootUrl}byoc/v3/collections/CUSTOM/searchIndex`;
const response = await this.fetchTiles(
searchIndexUrl,
bbox,
fromTime,
toTime,
Expand All @@ -101,23 +128,34 @@ export class BYOCLayer extends AbstractSentinelHubV3Layer {
};
}

protected getShServiceHostname(): string {
if (this.locationId === null) {
throw new Error('Parameter locationId must be specified');
}
const shServiceHostname = SHV3_LOCATIONS_ROOT_URL[this.locationId];
return shServiceHostname;
}

protected createSearchIndexRequestConfig(): AxiosRequestConfig {
return {};
}

protected async getFindDatesUTCUrl(): Promise<string> {
await this.updateLayerFromServiceIfNeeded();
const rootUrl = SHV3_LOCATIONS_ROOT_URL[this.locationId];
const findDatesUTCUrl = `${rootUrl}byoc/v3/collections/CUSTOM/findAvailableData`;
return findDatesUTCUrl;
}

protected async getFindDatesUTCAdditionalParameters(): Promise<Record<string, any>> {
if (this.shouldFetchAdditionalParams()) {
const layerParams = await this.fetchLayerParamsFromSHServiceV3();
this.collectionId = layerParams['collectionId'];
}
await this.updateLayerFromServiceIfNeeded();

const result: Record<string, any> = {
datasetParameters: {
type: this.dataset.datasetParametersType,
collectionId: this.collectionId,
},
};

return result;
}
}
1 change: 1 addition & 0 deletions src/layer/S1GRDAWSEULayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export class S1GRDAWSEULayer extends AbstractSentinelHubV3Layer {
};

const response = await this.fetchTiles(
this.dataset.searchIndexUrl,
bbox,
fromTime,
toTime,
Expand Down
9 changes: 8 additions & 1 deletion src/layer/S3OLCILayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@ export class S3OLCILayer extends AbstractSentinelHubV3Layer {
maxCount?: number,
offset?: number,
): Promise<PaginatedTiles> {
const response = await this.fetchTiles(bbox, fromTime, toTime, maxCount, offset);
const response = await this.fetchTiles(
this.dataset.searchIndexUrl,
bbox,
fromTime,
toTime,
maxCount,
offset,
);
return {
tiles: response.data.tiles.map(tile => ({
geometry: tile.dataGeometry,
Expand Down
1 change: 1 addition & 0 deletions src/layer/S3SLSTRLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export class S3SLSTRLayer extends AbstractSentinelHubV3Layer {
view: this.view,
};
const response = await this.fetchTiles(
this.dataset.searchIndexUrl,
bbox,
fromTime,
toTime,
Expand Down
1 change: 1 addition & 0 deletions src/layer/S5PL2Layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export class S5PL2Layer extends AbstractSentinelHubV3Layer {
// minQa: this.minQa,
};
const response = await this.fetchTiles(
this.dataset.searchIndexUrl,
bbox,
fromTime,
toTime,
Expand Down
15 changes: 15 additions & 0 deletions src/layer/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,21 @@ export const SH_SERVICE_HOSTNAMES_V3: string[] = [
'https://creodias.sentinel-hub.com/',
];

// See https://services.sentinel-hub.com/api/v1/metadata/location/ for an up-to-date
// list of location ids and the corresponding URLs.
export enum LocationIdSHv3 {
awsEuCentral1 = 'aws-eu-central-1',
awsUsWest2 = 'aws-us-west-2',
creo = 'creo',
mundi = 'mundi',
}
export const SHV3_LOCATIONS_ROOT_URL: Record<LocationIdSHv3, string> = {
[LocationIdSHv3.awsEuCentral1]: 'https://services.sentinel-hub.com/',
[LocationIdSHv3.awsUsWest2]: 'https://services-uswest2.sentinel-hub.com/',
[LocationIdSHv3.creo]: 'https://creodias.sentinel-hub.com/',
[LocationIdSHv3.mundi]: 'https://shservices.mundiwebservices.com/',
};

export type GetStatsParams = {
fromTime: Date;
toTime: Date;
Expand Down
6 changes: 3 additions & 3 deletions src/layer/dataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,9 @@ export const DATASET_BYOC: Dataset = {
shWmsEvalsource: 'CUSTOM',
shProcessingApiDatasourceAbbreviation: 'CUSTOM',
datasetParametersType: 'BYOC',
shServiceHostname: 'https://services.sentinel-hub.com/',
searchIndexUrl: 'https://services.sentinel-hub.com/byoc/v3/collections/CUSTOM/searchIndex',
findDatesUTCUrl: 'https://services.sentinel-hub.com/byoc/v3/collections/CUSTOM/findAvailableData',
shServiceHostname: null, // depends on location, for example: https://services.sentinel-hub.com/
searchIndexUrl: null, // depends on location, for example: https://services.sentinel-hub.com/byoc/v3/collections/CUSTOM/searchIndex
findDatesUTCUrl: null, // depends on location, for example: https://services.sentinel-hub.com/byoc/v3/collections/CUSTOM/findAvailableData
orbitTimeMinutes: null,
minDate: null,
maxDate: null,
Expand Down
Loading

0 comments on commit 5da276b

Please sign in to comment.