Skip to content

Commit ebd1f32

Browse files
Merge pull request #19 from sentinel-hub/feature/landsat-5-7-8-eocloud
Add Landsat 5, 7 and 8 at EO Cloud datasets and layers
2 parents 6592f31 + 4e682e5 commit ebd1f32

13 files changed

+645
-20
lines changed

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ S5PL2_LAYER_ID=
1515

1616
EOC_INSTANCE_ID=
1717
EOC_S1GRDIW_LAYER_ID=
18+
EOC_LANDSAT5_LAYER_ID=
19+
EOC_LANDSAT7_LAYER_ID=
20+
EOC_LANDSAT8_LAYER_ID=

src/index.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ import {
1111
DATASET_S2L1C,
1212
DATASET_S3SLSTR,
1313
DATASET_S3OLCI,
14+
DATASET_S5PL2,
1415
DATASET_AWS_L8L1C,
16+
DATASET_EOCLOUD_LANDSAT5,
17+
DATASET_EOCLOUD_LANDSAT7,
18+
DATASET_EOCLOUD_LANDSAT8,
1519
DATASET_MODIS,
1620
DATASET_AWS_DEM,
1721
} from 'src/layer/dataset';
@@ -25,12 +29,15 @@ import { S3OLCILayer } from 'src/layer/S3OLCILayer';
2529
import { S5PL2Layer } from 'src/layer/S5PL2Layer';
2630
import { MODISLayer } from 'src/layer/MODISLayer';
2731
import { DEMLayer } from 'src/layer/DEMLayer';
32+
import { Landsat5EOCloudLayer } from 'src/layer/Landsat5EOCloudLayer';
33+
import { Landsat7EOCloudLayer } from 'src/layer/Landsat7EOCloudLayer';
34+
import { Landsat8EOCloudLayer } from 'src/layer/Landsat8EOCloudLayer';
2835
import { Landsat8AWSLayer } from 'src/layer/Landsat8AWSLayer';
2936
import { BYOCLayer } from 'src/layer/BYOCLayer';
3037

3138
import { legacyGetMapFromUrl, legacyGetMapWmsUrlFromParams, legacyGetMapFromParams } from 'src/legacyCompat';
3239
import { AcquisitionMode, Polarization, Resolution, OrbitDirection } from 'src/layer/S1GRDAWSEULayer';
33-
import { registerAxiosCacheRetryInterceptors } from './utils/axiosInterceptors';
40+
import { registerAxiosCacheRetryInterceptors } from 'src/utils/axiosInterceptors';
3441

3542
registerAxiosCacheRetryInterceptors();
3643

@@ -42,7 +49,11 @@ export {
4249
DATASET_S2L1C,
4350
DATASET_S3SLSTR,
4451
DATASET_S3OLCI,
52+
DATASET_S5PL2,
4553
DATASET_AWS_L8L1C,
54+
DATASET_EOCLOUD_LANDSAT5,
55+
DATASET_EOCLOUD_LANDSAT7,
56+
DATASET_EOCLOUD_LANDSAT8,
4657
DATASET_MODIS,
4758
DATASET_AWS_DEM,
4859
// layers:
@@ -56,6 +67,9 @@ export {
5667
S5PL2Layer,
5768
MODISLayer,
5869
DEMLayer,
70+
Landsat5EOCloudLayer,
71+
Landsat7EOCloudLayer,
72+
Landsat8EOCloudLayer,
5973
Landsat8AWSLayer,
6074
BYOCLayer,
6175
// auth:

src/layer/AbstractSentinelHubV1OrV2Layer.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,11 @@ export class AbstractSentinelHubV1OrV2Layer extends AbstractLayer {
7676
crs: { type: 'name', properties: { name: bbox.crs.urn } },
7777
coordinates: [
7878
[
79-
[bbox.minY, bbox.maxX],
80-
[bbox.maxY, bbox.maxX],
81-
[bbox.maxY, bbox.minX],
82-
[bbox.minY, bbox.minX],
83-
[bbox.minY, bbox.maxX],
79+
[bbox.minX, bbox.minY],
80+
[bbox.maxX, bbox.minY],
81+
[bbox.maxX, bbox.maxY],
82+
[bbox.minX, bbox.maxY],
83+
[bbox.minX, bbox.minY],
8484
],
8585
],
8686
};
@@ -110,17 +110,22 @@ export class AbstractSentinelHubV1OrV2Layer extends AbstractLayer {
110110
};
111111
}
112112

113-
// Subclasses should override this helper method for LayersFactory.makeLayers. It constructs
114-
// a layer based on layerInfo and other parameters.
115-
public static makeLayer(
113+
// This helper method is called by LayersFactory.makeLayers(). It constructs
114+
// a layer based on layerInfo and other parameters. Subclasses can override it
115+
// to use different constructor parameters based on layerInfo.
116+
//
117+
// A bit of TypeScript magic: since we want to construct a child class from the static
118+
// method, we use the method outlined here: https://stackoverflow.com/a/51749145/593487
119+
public static makeLayer<ChildLayer extends typeof AbstractSentinelHubV1OrV2Layer>(
120+
this: ChildLayer,
116121
layerInfo: any, // eslint-disable-line @typescript-eslint/no-unused-vars
117-
instanceId: string, // eslint-disable-line @typescript-eslint/no-unused-vars
118-
layerId: string, // eslint-disable-line @typescript-eslint/no-unused-vars
119-
evalscript: string | null, // eslint-disable-line @typescript-eslint/no-unused-vars
120-
evalscriptUrl: string | null, // eslint-disable-line @typescript-eslint/no-unused-vars
121-
title: string | null, // eslint-disable-line @typescript-eslint/no-unused-vars
122-
description: string | null, // eslint-disable-line @typescript-eslint/no-unused-vars
122+
instanceId: string,
123+
layerId: string,
124+
evalscript: string | null,
125+
evalscriptUrl: string | null,
126+
title: string | null,
127+
description: string | null,
123128
): AbstractSentinelHubV1OrV2Layer {
124-
throw new Error('Not implemented');
129+
return new this(instanceId, layerId, evalscript, evalscriptUrl, title, description);
125130
}
126131
}

src/layer/Landsat5EOCloudLayer.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { DATASET_EOCLOUD_LANDSAT5 } from 'src/layer/dataset';
2+
import { AbstractSentinelHubV1OrV2Layer } from 'src/layer/AbstractSentinelHubV1OrV2Layer';
3+
4+
export class Landsat5EOCloudLayer extends AbstractSentinelHubV1OrV2Layer {
5+
public readonly dataset = DATASET_EOCLOUD_LANDSAT5;
6+
}

src/layer/Landsat7EOCloudLayer.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { DATASET_EOCLOUD_LANDSAT7 } from 'src/layer/dataset';
2+
import { AbstractSentinelHubV1OrV2Layer } from 'src/layer/AbstractSentinelHubV1OrV2Layer';
3+
4+
export class Landsat7EOCloudLayer extends AbstractSentinelHubV1OrV2Layer {
5+
public readonly dataset = DATASET_EOCLOUD_LANDSAT7;
6+
}

src/layer/Landsat8EOCloudLayer.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { DATASET_EOCLOUD_LANDSAT8 } from 'src/layer/dataset';
2+
import { AbstractSentinelHubV1OrV2Layer } from 'src/layer/AbstractSentinelHubV1OrV2Layer';
3+
4+
export class Landsat8EOCloudLayer extends AbstractSentinelHubV1OrV2Layer {
5+
public readonly dataset = DATASET_EOCLOUD_LANDSAT8;
6+
}

src/layer/LayersFactory.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import {
1414
DATASET_BYOC,
1515
DATASET_S5PL2,
1616
DATASET_EOCLOUD_S1GRD,
17+
DATASET_EOCLOUD_LANDSAT5,
18+
DATASET_EOCLOUD_LANDSAT7,
19+
DATASET_EOCLOUD_LANDSAT8,
1720
Dataset,
1821
} from 'src/layer/dataset';
1922
import { AbstractLayer } from 'src/layer/AbstractLayer';
@@ -29,6 +32,9 @@ import { DEMLayer } from 'src/layer/DEMLayer';
2932
import { Landsat8AWSLayer } from 'src/layer/Landsat8AWSLayer';
3033
import { BYOCLayer } from 'src/layer/BYOCLayer';
3134
import { S1GRDEOCloudLayer } from 'src/layer/S1GRDEOCloudLayer';
35+
import { Landsat5EOCloudLayer } from './Landsat5EOCloudLayer';
36+
import { Landsat7EOCloudLayer } from './Landsat7EOCloudLayer';
37+
import { Landsat8EOCloudLayer } from './Landsat8EOCloudLayer';
3238

3339
type GetCapabilitiesXml = {
3440
WMS_Capabilities: {
@@ -83,6 +89,9 @@ export class LayersFactory {
8389
S1: DATASET_EOCLOUD_S1GRD,
8490
S1_EW: DATASET_EOCLOUD_S1GRD,
8591
S1_EW_SH: DATASET_EOCLOUD_S1GRD,
92+
L5: DATASET_EOCLOUD_LANDSAT5,
93+
L7: DATASET_EOCLOUD_LANDSAT7,
94+
L8: DATASET_EOCLOUD_LANDSAT8,
8695
};
8796

8897
private static readonly LAYER_FROM_DATASET_V3 = {
@@ -100,6 +109,9 @@ export class LayersFactory {
100109

101110
private static readonly LAYER_FROM_DATASET_V12 = {
102111
[DATASET_EOCLOUD_S1GRD.id]: S1GRDEOCloudLayer,
112+
[DATASET_EOCLOUD_LANDSAT5.id]: Landsat5EOCloudLayer,
113+
[DATASET_EOCLOUD_LANDSAT7.id]: Landsat7EOCloudLayer,
114+
[DATASET_EOCLOUD_LANDSAT8.id]: Landsat8EOCloudLayer,
103115
};
104116

105117
private static async parseXml(xmlString: string): Promise<GetCapabilitiesXml> {

src/layer/S1GRDEOCloudLayer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { BackscatterCoeff } from 'src/layer/const';
22
import { DATASET_EOCLOUD_S1GRD } from 'src/layer/dataset';
33

4-
import { AbstractSentinelHubV1OrV2Layer } from './AbstractSentinelHubV1OrV2Layer';
4+
import { AbstractSentinelHubV1OrV2Layer } from 'src/layer/AbstractSentinelHubV1OrV2Layer';
55
import { AcquisitionMode, Polarization, Resolution } from 'src/layer/S1GRDAWSEULayer';
66

77
/*

src/layer/dataset.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,36 @@ export const DATASET_AWS_L8L1C: Dataset = {
8888
orbitTimeMinutes: 99,
8989
};
9090

91+
export const DATASET_EOCLOUD_LANDSAT5: Dataset = {
92+
id: 'EOC_L5',
93+
shJsonGetCapabilitiesDataset: null,
94+
shWmsEvalsource: 'L5',
95+
shProcessingApiDatasourceAbbreviation: null,
96+
shServiceHostname: 'https://eocloud.sentinel-hub.com/',
97+
searchIndexUrl: 'https://eocloud.sentinel-hub.com/index/landsat5/v2/search',
98+
orbitTimeMinutes: 99,
99+
};
100+
101+
export const DATASET_EOCLOUD_LANDSAT7: Dataset = {
102+
id: 'EOC_L7',
103+
shJsonGetCapabilitiesDataset: null,
104+
shWmsEvalsource: 'L7',
105+
shProcessingApiDatasourceAbbreviation: null,
106+
shServiceHostname: 'https://eocloud.sentinel-hub.com/',
107+
searchIndexUrl: 'https://eocloud.sentinel-hub.com/index/landsat7/v2/search',
108+
orbitTimeMinutes: 99,
109+
};
110+
111+
export const DATASET_EOCLOUD_LANDSAT8: Dataset = {
112+
id: 'EOC_L8',
113+
shJsonGetCapabilitiesDataset: null,
114+
shWmsEvalsource: 'L8',
115+
shProcessingApiDatasourceAbbreviation: null,
116+
shServiceHostname: 'https://eocloud.sentinel-hub.com/',
117+
searchIndexUrl: 'https://eocloud.sentinel-hub.com/index/landsat8/v2/search',
118+
orbitTimeMinutes: 99,
119+
};
120+
91121
export const DATASET_MODIS: Dataset = {
92122
id: 'AWS_MODIS',
93123
shJsonGetCapabilitiesDataset: 'MODIS',

stories/landsat5.eocloud.stories.js

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import {
2+
Landsat5EOCloudLayer,
3+
CRS_EPSG3857,
4+
BBox,
5+
MimeTypes,
6+
ApiType,
7+
DATASET_EOCLOUD_LANDSAT5,
8+
LayersFactory,
9+
} from '../dist/sentinelHub.esm';
10+
11+
if (!process.env.EOC_INSTANCE_ID) {
12+
throw new Error("EOC_INSTANCE_ID environment variable is not defined!");
13+
};
14+
15+
if (!process.env.EOC_LANDSAT5_LAYER_ID) {
16+
throw new Error("EOC_LANDSAT5_LAYER_ID environment variable is not defined!");
17+
};
18+
19+
const instanceId = process.env.EOC_INSTANCE_ID;
20+
const layerId = process.env.EOC_LANDSAT5_LAYER_ID;
21+
const bbox = new BBox(CRS_EPSG3857, 1487158.82, 5322463.15, 1565430.34, 5400734.67);
22+
23+
export default {
24+
title: 'Landsat 5 - EOCloud',
25+
};
26+
27+
export const getMapURL = () => {
28+
const img = document.createElement('img');
29+
img.width = '512';
30+
img.height = '512';
31+
32+
const wrapperEl = document.createElement('div');
33+
wrapperEl.innerHTML = "<h2>GetMapUrl (WMS)</h2>";
34+
wrapperEl.insertAdjacentElement("beforeend", img);
35+
36+
const layer = new Landsat5EOCloudLayer(instanceId, layerId);
37+
38+
const getMapParams = {
39+
bbox: bbox,
40+
fromTime: new Date(Date.UTC(2010, 11 - 1, 22, 0, 0, 0)),
41+
toTime: new Date(Date.UTC(2010, 12 - 1, 22, 23, 59, 59)),
42+
width: 512,
43+
height: 512,
44+
format: MimeTypes.JPEG,
45+
};
46+
const imageUrl = layer.getMapUrl(getMapParams, ApiType.WMS);
47+
img.src = imageUrl;
48+
49+
return wrapperEl;
50+
};
51+
52+
export const getMapWMS = () => {
53+
const img = document.createElement('img');
54+
img.width = '512';
55+
img.height = '512';
56+
57+
const wrapperEl = document.createElement('div');
58+
wrapperEl.innerHTML = "<h2>GetMap with WMS</h2>";
59+
wrapperEl.insertAdjacentElement("beforeend", img);
60+
61+
const perform = async () => {
62+
const layer = new Landsat5EOCloudLayer(instanceId, layerId);
63+
64+
const getMapParams = {
65+
bbox: bbox,
66+
fromTime: new Date(Date.UTC(2010, 11 - 1, 22, 0, 0, 0)),
67+
toTime: new Date(Date.UTC(2010, 12 - 1, 22, 23, 59, 59)),
68+
width: 512,
69+
height: 512,
70+
format: MimeTypes.JPEG,
71+
};
72+
const imageBlob = await layer.getMap(getMapParams, ApiType.WMS);
73+
img.src = URL.createObjectURL(imageBlob);
74+
};
75+
perform().then(() => {});
76+
77+
return wrapperEl;
78+
};
79+
80+
export const getMapWMSLayersFactory = () => {
81+
const img = document.createElement('img');
82+
img.width = '512';
83+
img.height = '512';
84+
85+
const wrapperEl = document.createElement('div');
86+
wrapperEl.innerHTML = "<h2>GetMap with WMS</h2>";
87+
wrapperEl.insertAdjacentElement("beforeend", img);
88+
89+
const perform = async () => {
90+
const layer = (await LayersFactory.makeLayers(`${DATASET_EOCLOUD_LANDSAT5.shServiceHostname}v1/wms/${instanceId}`, (lId, datasetId) => (layerId === lId)))[0];
91+
92+
const getMapParams = {
93+
bbox: bbox,
94+
fromTime: new Date(Date.UTC(2010, 11 - 1, 22, 0, 0, 0)),
95+
toTime: new Date(Date.UTC(2010, 12 - 1, 22, 23, 59, 59)),
96+
width: 512,
97+
height: 512,
98+
format: MimeTypes.JPEG,
99+
};
100+
const imageBlob = await layer.getMap(getMapParams, ApiType.WMS);
101+
img.src = URL.createObjectURL(imageBlob);
102+
};
103+
perform().then(() => {});
104+
105+
return wrapperEl;
106+
};
107+
108+
export const getMapWMSEvalscript = () => {
109+
const img = document.createElement('img');
110+
img.width = '512';
111+
img.height = '512';
112+
113+
const wrapperEl = document.createElement('div');
114+
wrapperEl.innerHTML = "<h2>GetMap with WMS - evalscript</h2>";
115+
wrapperEl.insertAdjacentElement("beforeend", img);
116+
117+
const perform = async () => {
118+
const layer = new Landsat5EOCloudLayer(
119+
instanceId,
120+
layerId,
121+
`
122+
return [2.5 * B04, 1.5 * B03, 0.5 * B02];
123+
`,
124+
);
125+
126+
const getMapParams = {
127+
bbox: bbox,
128+
fromTime: new Date(Date.UTC(2010, 11 - 1, 22, 0, 0, 0)),
129+
toTime: new Date(Date.UTC(2010, 12 - 1, 22, 23, 59, 59)),
130+
width: 512,
131+
height: 512,
132+
format: MimeTypes.JPEG,
133+
maxCCPercent: 100,
134+
};
135+
const imageBlob = await layer.getMap(getMapParams, ApiType.WMS);
136+
img.src = URL.createObjectURL(imageBlob);
137+
};
138+
perform().then(() => {});
139+
140+
return wrapperEl;
141+
};
142+
143+
export const findTiles = () => {
144+
const layer = new Landsat5EOCloudLayer(instanceId, layerId);
145+
const containerEl = document.createElement('pre');
146+
147+
const wrapperEl = document.createElement('div');
148+
wrapperEl.innerHTML = "<h2>findTiles</h2>";
149+
wrapperEl.insertAdjacentElement("beforeend", containerEl);
150+
151+
const perform = async () => {
152+
const data = await layer.findTiles(
153+
bbox,
154+
new Date(Date.UTC(2000, 1 - 1, 1, 0, 0, 0)),
155+
new Date(Date.UTC(2020, 1 - 1, 15, 23, 59, 59)),
156+
5,
157+
null,
158+
);
159+
renderTilesList(containerEl, data.tiles);
160+
};
161+
perform().then(() => {});
162+
163+
return wrapperEl;
164+
};
165+
166+
function renderTilesList(containerEl, list) {
167+
list.forEach(tile => {
168+
const ul = document.createElement('ul');
169+
containerEl.appendChild(ul);
170+
for (let key in tile) {
171+
const li = document.createElement('li');
172+
ul.appendChild(li);
173+
let text;
174+
if (tile[key] instanceof Object) {
175+
text = JSON.stringify(tile[key]);
176+
} else {
177+
text = tile[key];
178+
}
179+
li.innerHTML = `${key} : ${text}`;
180+
}
181+
});
182+
}

0 commit comments

Comments
 (0)