Skip to content

Commit

Permalink
Merge pull request #259 from sentinel-hub/feature/low-res-byoc
Browse files Browse the repository at this point in the history
Feature/low res byoc
  • Loading branch information
mitjak authored Feb 7, 2024
2 parents 245343d + b937370 commit a63cbd3
Show file tree
Hide file tree
Showing 7 changed files with 294 additions and 17 deletions.
3 changes: 2 additions & 1 deletion src/layer/AbstractSentinelHubV3Layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export class AbstractSentinelHubV3Layer extends AbstractLayer {
payload: ProcessingPayload,
datasetSeqNo: number = 0, // eslint-disable-line @typescript-eslint/no-unused-vars
reqConfig?: RequestConfiguration, // eslint-disable-line @typescript-eslint/no-unused-vars
params?: GetMapParams, // eslint-disable-line @typescript-eslint/no-unused-vars
): Promise<ProcessingPayload> {
// Subclasses should override this method if they wish to supply additional
// parameters to Processing API.
Expand Down Expand Up @@ -214,7 +215,7 @@ export class AbstractSentinelHubV3Layer extends AbstractLayer {
this.downsampling,
);
// allow subclasses to update payload with their own parameters:
const updatedPayload = await this._updateProcessingGetMapPayload(payload, 0, innerReqConfig);
const updatedPayload = await this._updateProcessingGetMapPayload(payload, 0, innerReqConfig, params);
const shServiceHostname = this.getShServiceHostname();
let blob = await processingGetMap(shServiceHostname, updatedPayload, innerReqConfig);

Expand Down
28 changes: 26 additions & 2 deletions src/layer/BYOCLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { getAxiosReqParams, RequestConfiguration } from '../utils/cancelRequests
import { ensureTimeout } from '../utils/ensureTimeout';
import { CACHE_CONFIG_30MIN } from '../utils/cacheHandlers';
import { StatisticsProviderType } from '../statistics/StatisticsProvider';
import { getSHServiceRootUrl } from './utils';
import { getSHServiceRootUrl, metersPerPixel } from './utils';

interface ConstructorParameters {
instanceId?: string | null;
Expand All @@ -53,6 +53,8 @@ export class BYOCLayer extends AbstractSentinelHubV3Layer {
public locationId: LocationIdSHv3;
public subType: BYOCSubTypes;
public shServiceRootUrl: string;
public lowResolutionCollectionId: string;
public lowResolutionMetersPerPixelThreshold: number;

public constructor({
instanceId = null,
Expand Down Expand Up @@ -141,12 +143,27 @@ export class BYOCLayer extends AbstractSentinelHubV3Layer {
payload: ProcessingPayload,
datasetSeqNo: number = 0,
reqConfig?: RequestConfiguration,
params?: GetMapParams,
): Promise<ProcessingPayload> {
await this.updateLayerFromServiceIfNeeded(reqConfig);
payload.input.data[datasetSeqNo].type = this.getTypeId();

if (this.shouldUseLowResolutionCollection(params?.bbox, payload.output.width)) {
payload.input.data[datasetSeqNo].type = this.getTypeIdLowRes();
} else {
payload.input.data[datasetSeqNo].type = this.getTypeId();
}

return payload;
}

public shouldUseLowResolutionCollection(bbox: BBox, width: number): boolean {
return (
this.lowResolutionCollectionId !== undefined &&
this.lowResolutionMetersPerPixelThreshold !== undefined &&
metersPerPixel(bbox, width) > this.lowResolutionMetersPerPixelThreshold
);
}

protected convertResponseFromSearchIndex(response: {
data: { tiles: any[]; hasMore: boolean };
}): PaginatedTiles {
Expand Down Expand Up @@ -208,6 +225,10 @@ export class BYOCLayer extends AbstractSentinelHubV3Layer {
return `${this.getTypePrefix()}-${this.collectionId}`;
}

protected getTypeIdLowRes(): string {
return `${this.getTypePrefix()}-${this.lowResolutionCollectionId}`;
}

protected getTypePrefix(): string {
switch (this.subType) {
case BYOCSubTypes.BATCH:
Expand All @@ -220,6 +241,9 @@ export class BYOCLayer extends AbstractSentinelHubV3Layer {
}

protected getCatalogCollectionId(): string {
if (this.lowResolutionCollectionId !== undefined) {
return this.getTypeIdLowRes();
}
return this.getTypeId();
}

Expand Down
139 changes: 137 additions & 2 deletions src/layer/__tests__/BYOCLayer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BBox, CRS_EPSG4326, setAuthToken, LocationIdSHv3, BYOCLayer } from '../../index';
import { CRS_EPSG4326, setAuthToken, LocationIdSHv3, BYOCLayer, CRS_EPSG3857, BBox } from '../../index';
import { SHV3_LOCATIONS_ROOT_URL, BYOCSubTypes, SH_SERVICE_ROOT_URL } from '../const';
import { constructFixtureFindTilesSearchIndex, constructFixtureFindTilesCatalog } from './fixtures.BYOCLayer';

Expand Down Expand Up @@ -231,7 +231,7 @@ describe('Test updateLayerFromServiceIfNeeded for ZARR', () => {
});
});

describe.only('shServiceRootUrl', () => {
describe('shServiceRootUrl', () => {
beforeEach(async () => {
setAuthToken(AUTH_TOKEN);
mockNetwork.reset();
Expand Down Expand Up @@ -287,3 +287,138 @@ describe.only('shServiceRootUrl', () => {
expect(layer.getSHServiceRootUrl()).toBe(expected);
});
});

describe('shouldUseLowResolutionCollection', () => {
beforeEach(async () => {
setAuthToken(AUTH_TOKEN);
mockNetwork.reset();
});

test.each([
{
layerParams: {
instanceId: 'INSTANCE_ID',
layerId: 'LAYER_ID',
collectionId: 'mockCollectionId',
subType: BYOCSubTypes.BYOC,
},
lowResolutionCollectionId: 'LOW_RESOLUTION_COLLECTION_ID',
lowResolutionMetersPerPixelThreshold: 300,
bbox: new BBox(
CRS_EPSG4326,
-114.27429199218751,
45.85176048817254,
-112.17864990234376,
48.21003212234042,
),
width: 512,
expected: true,
},
{
layerParams: {
instanceId: 'INSTANCE_ID',
layerId: 'LAYER_ID',
collectionId: 'mockCollectionId',
subType: BYOCSubTypes.BYOC,
},
lowResolutionCollectionId: 'LOW_RESOLUTION_COLLECTION_ID',
lowResolutionMetersPerPixelThreshold: 300,
bbox: new BBox(
CRS_EPSG3857,
-12601714.2312073,
5870363.772301538,
-12523442.714243278,
5948635.289265559,
),
width: 512,
expected: false,
},
{
layerParams: {
instanceId: 'INSTANCE_ID',
layerId: 'LAYER_ID',
collectionId: 'mockCollectionId',
subType: BYOCSubTypes.BYOC,
},
lowResolutionCollectionId: 'LOW_RESOLUTION_COLLECTION_ID',
lowResolutionMetersPerPixelThreshold: 300,
bbox: new BBox(
CRS_EPSG3857,
-15028131.257091936,
2504688.542848655,
-12523442.714243278,
5009377.085697314,
),
width: 512,
expected: true,
},
{
layerParams: {
instanceId: 'INSTANCE_ID',
layerId: 'LAYER_ID',
collectionId: 'mockCollectionId',
subType: BYOCSubTypes.BYOC,
},
lowResolutionCollectionId: 'LOW_RESOLUTION_COLLECTION_ID',
lowResolutionMetersPerPixelThreshold: 300,
bbox: new BBox(
CRS_EPSG3857,
112.81332057952881,
63.97041521013803,
119.85694837570192,
65.98227733565385,
),
width: 512,
expected: false,
},
{
layerParams: {
instanceId: 'INSTANCE_ID',
layerId: 'LAYER_ID',
collectionId: 'mockCollectionId',
subType: BYOCSubTypes.BYOC,
},
bbox: new BBox(
CRS_EPSG3857,
-15028131.257091936,
2504688.542848655,
-12523442.714243278,
5009377.085697314,
),
width: 512,
expected: false,
},
{
layerParams: {
instanceId: 'INSTANCE_ID',
layerId: 'LAYER_ID',
collectionId: 'mockCollectionId',
subType: BYOCSubTypes.BYOC,
},
bbox: new BBox(
CRS_EPSG3857,
112.81332057952881,
63.97041521013803,
119.85694837570192,
65.98227733565385,
),
width: 512,
expected: false,
},
])(
'shouldUseLowResolutionCollection %p',
async ({
layerParams,
lowResolutionCollectionId,
lowResolutionMetersPerPixelThreshold,
bbox,
width,
expected,
}) => {
const layer = new BYOCLayer(layerParams);
layer.lowResolutionCollectionId = lowResolutionCollectionId;
layer.lowResolutionMetersPerPixelThreshold = lowResolutionMetersPerPixelThreshold;
expect(layer.shouldUseLowResolutionCollection(bbox, width)).toBe(expected);
},
);
});
99 changes: 98 additions & 1 deletion src/layer/__tests__/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { BBox } from '../../bbox';
import { CRS_EPSG3857, CRS_EPSG4326 } from '../../crs';
import { OgcServiceTypes, SH_SERVICE_ROOT_URL } from '../const';
import { createGetCapabilitiesXmlUrl, getSHServiceRootUrlFromBaseUrl } from '../utils';
import {
createGetCapabilitiesXmlUrl,
ensureMercatorBBox,
getSHServiceRootUrlFromBaseUrl,
metersPerPixel,
} from '../utils';

const cases = [
{
Expand Down Expand Up @@ -42,3 +49,93 @@ describe('getSHServiceRootUrlFromBaseUrl', () => {
expect(getSHServiceRootUrlFromBaseUrl(baseUrl)).toBe(expected);
});
});

describe('ensureMercatorBBox', () => {
test.each([
{
bbox: new BBox(
CRS_EPSG3857,
112.81332057952881,
63.97041521013803,
119.85694837570192,
65.98227733565385,
),
expected: new BBox(
CRS_EPSG3857,
112.81332057952881,
63.97041521013803,
119.85694837570192,
65.98227733565385,
),
},
{
bbox: new BBox(
CRS_EPSG4326,
-114.27429199218751,
45.85176048817254,
-112.17864990234376,
48.21003212234042,
),
expected: new BBox(
CRS_EPSG3857,
-12720955.995332174,
5756625.474213193,
-12487670.185005816,
6141868.096770483,
),
},
])('ensureMercatorBBox %p', ({ bbox, expected }) => {
expect(ensureMercatorBBox(bbox)).toMatchObject(expected);
});
});

describe('metersPerPixel', () => {
test.each([
{
bbox: new BBox(
CRS_EPSG3857,
112.81332057952881,
63.97041521013803,
119.85694837570192,
65.98227733565385,
),
width: 512,
expected: 0.01375708553868674,
},
{
bbox: new BBox(
CRS_EPSG3857,
-15028131.257091936,
2504688.542848655,
-12523442.714243278,
5009377.085697314,
),
width: 512,
expected: 4150.788658570558,
},
{
bbox: new BBox(
CRS_EPSG3857,
-12601714.2312073,
5870363.772301538,
-12523442.714243278,
5948635.289265559,
),
width: 512,
expected: 104.64937737265413,
},
{
bbox: new BBox(
CRS_EPSG4326,
-114.27429199218751,
45.85176048817254,
-112.17864990234376,
48.21003212234042,
),
width: 512,
expected: 310.4876808881625,
},
])('metersPerPixel %p', ({ bbox, width, expected }) => {
expect(metersPerPixel(bbox, width)).toEqual(expected);
});
});
28 changes: 27 additions & 1 deletion src/layer/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import { parseStringPromise } from 'xml2js';
import { OgcServiceTypes, SH_SERVICE_HOSTNAMES_V3, SH_SERVICE_ROOT_URL } from './const';
import { getAxiosReqParams, RequestConfiguration } from '../utils/cancelRequests';
import { CACHE_CONFIG_30MIN, CACHE_CONFIG_30MIN_MEMORY } from '../utils/cacheHandlers';
import { GetCapabilitiesWmtsXml } from './wmts.utils';
import { EQUATOR_RADIUS, GetCapabilitiesWmtsXml } from './wmts.utils';
import { getAuthToken } from '../auth';
import { BBox } from '../bbox';
import { CRS_EPSG3857 } from '../crs';
import proj4 from 'proj4';

interface Capabilities {
Service: [];
Expand Down Expand Up @@ -225,3 +228,26 @@ export async function fetchLayerParamsFromConfigurationService(
}));
return layersParams;
}

export function ensureMercatorBBox(bbox: BBox): BBox {
if (bbox.crs.authId === CRS_EPSG3857.authId) {
return bbox;
}

const [minX, minY] = proj4(bbox.crs.authId, CRS_EPSG3857.authId, [bbox.minX, bbox.minY]);
const [maxX, maxY] = proj4(bbox.crs.authId, CRS_EPSG3857.authId, [bbox.maxX, bbox.maxY]);
return new BBox(CRS_EPSG3857, minX, minY, maxX, maxY);
}

export function metersPerPixel(bbox: BBox, width: number): number {
const newBBox = ensureMercatorBBox(bbox);

const widthInMeters = Math.abs(newBBox.maxX - newBBox.minX);
const latitude = (newBBox.minY + newBBox.maxY) / 2;

return (widthInMeters / width) * Math.cos(lat(latitude));
}

function lat(y: number): number {
return 2 * (Math.PI / 4 - Math.atan(Math.exp(-y / EQUATOR_RADIUS)));
}
Loading

0 comments on commit a63cbd3

Please sign in to comment.