diff --git a/src/layer/AbstractDEMLayer.ts b/src/layer/AbstractDEMLayer.ts index 65c1d24e..a6f114dd 100644 --- a/src/layer/AbstractDEMLayer.ts +++ b/src/layer/AbstractDEMLayer.ts @@ -21,6 +21,7 @@ export interface ConstructorParameters { demInstance?: DEMInstanceType | null; egm?: boolean | null; clampNegative?: boolean | null; + highlights?: AbstractSentinelHubV3Layer['highlights']; } export class AbstractDEMLayer extends AbstractSentinelHubV3Layer { diff --git a/src/layer/AbstractSentinelHubV3Layer.ts b/src/layer/AbstractSentinelHubV3Layer.ts index 4eabcec2..2f4a095f 100644 --- a/src/layer/AbstractSentinelHubV3Layer.ts +++ b/src/layer/AbstractSentinelHubV3Layer.ts @@ -22,6 +22,7 @@ import { PaginatedTiles, Stats, SUPPORTED_DATA_PRODUCTS_PROCESSING, + Highlight, } from './const'; import { createProcessingPayload, processingGetMap, ProcessingPayload } from './processing'; import { wmsGetMapUrl } from './wms'; @@ -46,6 +47,7 @@ interface ConstructorParameters { upsampling?: Interpolator | null; downsampling?: Interpolator | null; legendUrl?: string | null; + highlights?: Highlight[] | null; } // this class provides any SHv3-specific functionality to the subclasses: @@ -59,6 +61,7 @@ export class AbstractSentinelHubV3Layer extends AbstractLayer { public mosaickingOrder: MosaickingOrder | null; // public because ProcessingDataFusionLayer needs to read it directly public upsampling: Interpolator | null; public downsampling: Interpolator | null; + public highlights?: Highlight[] | null; public constructor({ instanceId = null, @@ -72,6 +75,7 @@ export class AbstractSentinelHubV3Layer extends AbstractLayer { upsampling = null, downsampling = null, legendUrl = null, + highlights = null, }: ConstructorParameters) { super({ title, description, legendUrl }); if ( @@ -92,6 +96,7 @@ export class AbstractSentinelHubV3Layer extends AbstractLayer { this.mosaickingOrder = mosaickingOrder; this.upsampling = upsampling; this.downsampling = downsampling; + this.highlights = highlights; } public getLayerId(): string { @@ -117,11 +122,11 @@ export class AbstractSentinelHubV3Layer extends AbstractLayer { if (!this.dataset) { throw new Error('This layer does not support Processing API (unknown dataset)'); } - const layersParams = await fetchLayerParamsFromConfigurationService( - this.getSHServiceRootUrl(), - this.instanceId, + const layersParams = await fetchLayerParamsFromConfigurationService({ + shServiceHostName: this.getSHServiceRootUrl(), + instanceId: this.instanceId, reqConfig, - ); + }); const layerParams = layersParams.find((l: any) => l.layerId === this.layerId); if (!layerParams) { @@ -697,6 +702,8 @@ export class AbstractSentinelHubV3Layer extends AbstractLayer { }, reqConfig); } + public async updateLayerAndHighlightsFromService(reqConfig?: RequestConfiguration): Promise {} + protected getFindTilesAdditionalParameters(): FindTilesAdditionalParameters { return { maxCloudCoverPercent: null, diff --git a/src/layer/BYOCLayer.ts b/src/layer/BYOCLayer.ts index fde5cd96..161ff51f 100644 --- a/src/layer/BYOCLayer.ts +++ b/src/layer/BYOCLayer.ts @@ -40,6 +40,7 @@ interface ConstructorParameters { locationId?: LocationIdSHv3 | null; subType?: BYOCSubTypes | null; shServiceRootUrl?: string; + highlights?: AbstractSentinelHubV3Layer['highlights']; } type BYOCFindTilesDatasetParameters = { @@ -68,8 +69,9 @@ export class BYOCLayer extends AbstractSentinelHubV3Layer { locationId = null, subType = null, shServiceRootUrl = SH_SERVICE_ROOT_URL.default, + highlights = null, }: ConstructorParameters) { - super({ instanceId, layerId, evalscript, evalscriptUrl, dataProduct, title, description }); + super({ instanceId, layerId, evalscript, evalscriptUrl, dataProduct, title, description, highlights }); this.collectionId = collectionId; this.locationId = locationId; this.subType = subType; diff --git a/src/layer/HLSAWSLayer.ts b/src/layer/HLSAWSLayer.ts index 610186a8..7dad4112 100644 --- a/src/layer/HLSAWSLayer.ts +++ b/src/layer/HLSAWSLayer.ts @@ -16,6 +16,7 @@ interface ConstructorParameters { legendUrl?: string | null; maxCloudCoverPercent?: number | null; constellation?: HLSConstellation | null; + highlights?: AbstractSentinelHubV3WithCCLayer['highlights']; } type HLSFindTilesDatasetParameters = { diff --git a/src/layer/LayersFactory.ts b/src/layer/LayersFactory.ts index 27c132cb..8b2ba62f 100644 --- a/src/layer/LayersFactory.ts +++ b/src/layer/LayersFactory.ts @@ -191,6 +191,7 @@ export class LayersFactory { overrideConstructorParams?: Record | null, reqConfig?: RequestConfiguration, preferGetCapabilities: boolean = true, + includeHighlights: boolean = false, ): Promise { const returnValue = await ensureTimeout(async (innerReqConfig) => { for (let hostname of SH_SERVICE_HOSTNAMES_V3) { @@ -201,6 +202,7 @@ export class LayersFactory { overrideConstructorParams, innerReqConfig, preferGetCapabilities, + includeHighlights, ); } } @@ -228,12 +230,14 @@ export class LayersFactory { overrideConstructorParams: Record | null, reqConfig: RequestConfiguration, preferGetCapabilities: boolean = true, + includeHighlights: boolean = false, ): Promise { const filteredLayersInfos = await this.getSHv3LayersInfo( baseUrl, reqConfig, filterLayers, preferGetCapabilities, + includeHighlights, ); return filteredLayersInfos.map( @@ -271,6 +275,7 @@ export class LayersFactory { reqConfig: RequestConfiguration, filterLayers: Function, preferGetCapabilities: boolean = true, + includeHighlights: boolean = false, ): Promise { let layersInfos; //also check if auth token is present @@ -279,11 +284,12 @@ export class LayersFactory { // use configuration if possible if (authToken && preferGetCapabilities === false) { try { - const layers = await fetchLayerParamsFromConfigurationService( - getSHServiceRootUrlFromBaseUrl(baseUrl), - parseSHInstanceId(baseUrl), + const layers = await fetchLayerParamsFromConfigurationService({ + shServiceHostName: getSHServiceRootUrlFromBaseUrl(baseUrl), + instanceId: parseSHInstanceId(baseUrl), + includeHighlights, reqConfig, - ); + }); layersInfos = layers.map((l: any) => ({ ...l, dataset: LayersFactory.matchDatasetFromGetCapabilities(l.type, baseUrl), diff --git a/src/layer/ProcessingDataFusionLayer.ts b/src/layer/ProcessingDataFusionLayer.ts index feb26f29..8b9089b7 100644 --- a/src/layer/ProcessingDataFusionLayer.ts +++ b/src/layer/ProcessingDataFusionLayer.ts @@ -32,6 +32,7 @@ interface ConstructorParameters { layers: DataFusionLayerInfo[]; title?: string | null; description?: string | null; + highlights?: AbstractSentinelHubV3Layer['highlights']; } export type DataFusionLayerInfo = { @@ -54,8 +55,9 @@ export class ProcessingDataFusionLayer extends AbstractSentinelHubV3Layer { evalscript = null, evalscriptUrl = null, layers, + highlights = null, }: ConstructorParameters) { - super({ title, description, evalscript, evalscriptUrl }); + super({ title, description, evalscript, evalscriptUrl, highlights }); this.layers = layers; } diff --git a/src/layer/S1GRDAWSEULayer.ts b/src/layer/S1GRDAWSEULayer.ts index 3cfb3d8a..eb4c8661 100644 --- a/src/layer/S1GRDAWSEULayer.ts +++ b/src/layer/S1GRDAWSEULayer.ts @@ -71,6 +71,7 @@ interface ConstructorParameters { backscatterCoeff?: BackscatterCoeff | null; orbitDirection?: OrbitDirection | null; speckleFilter?: SpeckleFilter | null; + highlights?: AbstractSentinelHubV3Layer['highlights']; } type S1GRDFindTilesDatasetParameters = { @@ -112,8 +113,19 @@ export class S1GRDAWSEULayer extends AbstractSentinelHubV3Layer { orbitDirection = null, speckleFilter = null, mosaickingOrder = null, + highlights = null, }: ConstructorParameters) { - super({ instanceId, layerId, evalscript, evalscriptUrl, dataProduct, title, description, legendUrl }); + super({ + instanceId, + layerId, + evalscript, + evalscriptUrl, + dataProduct, + title, + description, + legendUrl, + highlights, + }); this.acquisitionMode = acquisitionMode; this.polarization = polarization; this.resolution = resolution; diff --git a/src/layer/S3SLSTRLayer.ts b/src/layer/S3SLSTRLayer.ts index 2e053bf1..e98b8806 100644 --- a/src/layer/S3SLSTRLayer.ts +++ b/src/layer/S3SLSTRLayer.ts @@ -24,6 +24,7 @@ interface ConstructorParameters { legendUrl?: string | null; maxCloudCoverPercent?: number | null; view?: S3SLSTRView | null; + highlights?: AbstractSentinelHubV3WithCCLayer['highlights']; } export enum S3SLSTRView { diff --git a/src/layer/S5PL2Layer.ts b/src/layer/S5PL2Layer.ts index 657a418e..69447a90 100644 --- a/src/layer/S5PL2Layer.ts +++ b/src/layer/S5PL2Layer.ts @@ -37,6 +37,7 @@ interface ConstructorParameters { productType?: ProductType | null; maxCloudCoverPercent?: number | null; minQa?: number | null; + highlights?: AbstractSentinelHubV3Layer['highlights']; } type S5PL2FindTilesDatasetParameters = { @@ -63,8 +64,19 @@ export class S5PL2Layer extends AbstractSentinelHubV3Layer { productType = null, maxCloudCoverPercent = 100, minQa = null, + highlights = null, }: ConstructorParameters) { - super({ instanceId, layerId, evalscript, evalscriptUrl, dataProduct, title, description, legendUrl }); + super({ + instanceId, + layerId, + evalscript, + evalscriptUrl, + dataProduct, + title, + description, + legendUrl, + highlights, + }); this.productType = productType; this.maxCloudCoverPercent = maxCloudCoverPercent; this.minQa = minQa; diff --git a/src/layer/const.ts b/src/layer/const.ts index 25577d01..e3d73887 100644 --- a/src/layer/const.ts +++ b/src/layer/const.ts @@ -297,3 +297,15 @@ export const XmlParserOptions = Object.freeze({ return isA; }, }); + +export interface Highlight { + id: string; + layerId: string; + instanceId: string; + title: string; + description: string; + areaOfInterest: object; + fromTime: string; + toTime: string; + orderHint: string; +} diff --git a/src/layer/utils.ts b/src/layer/utils.ts index 8071f0db..4c9ee65b 100644 --- a/src/layer/utils.ts +++ b/src/layer/utils.ts @@ -14,6 +14,7 @@ import { SH_SERVICE_HOSTNAMES_V3, SH_SERVICE_ROOT_URL, XmlParserOptions, + Highlight, } from './const'; import { GetCapabilitiesWmtsXml } from './wmts.utils'; @@ -188,17 +189,27 @@ export function getSHServiceRootUrlFromBaseUrl(baseUrl: string): string { return getSHServiceRootUrl(host); } -export async function fetchLayerParamsFromConfigurationService( - shServiceHostName: string, - instanceId: string, - reqConfig: RequestConfiguration, -): Promise { +export async function fetchLayerParamsFromConfigurationService({ + shServiceHostName, + instanceId, + includeHighlights, + reqConfig, +}: { + shServiceHostName: string; + instanceId: string; + includeHighlights?: boolean; + reqConfig: RequestConfiguration; +}): Promise { const authToken = reqConfig && reqConfig.authToken ? reqConfig.authToken : getAuthToken(); if (!authToken) { throw new Error('Must be authenticated to fetch layer params'); } const configurationServiceHostName = shServiceHostName ?? SH_SERVICE_ROOT_URL.default; - const url = `${configurationServiceHostName}api/v2/configuration/instances/${instanceId}/layers`; + const url = new URL(`${configurationServiceHostName}api/v2/configuration/instances/${instanceId}/layers`); + if (includeHighlights) { + url.searchParams.set('listHighlights', 'true'); + } + const headers = { Authorization: `Bearer ${authToken}`, }; @@ -218,7 +229,7 @@ export async function fetchLayerParamsFromConfigurationService( headers: headers, ...getAxiosReqParams(reqConfigWithMemoryCache, null), }; - const res = await axios.get(url, requestConfig); + const res = await axios.get(url.toString(), requestConfig); const layersParams = res.data.map((l: any) => { const defaultStyle = l.styles.find((s: any) => s.name === l.defaultStyleName) ?? l.styles[0]; @@ -236,6 +247,7 @@ export async function fetchLayerParamsFromConfigurationService( ? `${configurationServiceHostName}api/v2/configuration/datasets/${l.collectionType}/dataproducts/${defaultStyle.dataProductId}` : undefined, legend: defaultStyle ? defaultStyle.legend : null, + highlights: l.highlights?.member.map((h: Highlight) => ({ ...h, datasetId: l.collectionType })), }; }); return layersParams;