Skip to content

Commit

Permalink
Merge pull request #12 from sentinel-hub/feature/flyovers
Browse files Browse the repository at this point in the history
findFlyoverIntervals(tiles) - function for finding flyover intervals for given tiles
  • Loading branch information
zcernigoj authored Feb 6, 2020
2 parents 3af24d8 + 417311e commit 53320b4
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 7 deletions.
135 changes: 135 additions & 0 deletions DatasetsInfo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Dataset information

Most important info is Period / orbit time which is used for finding flyover times of orbits over a chosen area.
Orbit time is the time in which the satellite makes 1 circle around the Earth (or in the missions with 2 satellites, half the circle around the Earth as both satellites travel on the same track with approximately 180° difference).

# Copernicus

Copernicus missions consist of 1 or 2 satellites.
In mission with 2 satellites, the satellites are approximately 180° apart.
In that case the orbit time needs to be halfed as both satellites are traveling on (almost) the same track.

## Sentinel-1

Consists of 2 satellites: Sentinel-1A, Sentinel-1B

- 1 satellite:
- Orbital cycle: 12 days
- Orbits per cycle: 175
- Calculated time for 1 orbit: ~98.742 min (should be the same as Period)
- Period (time to circle the Earth 1 time = 1 orbit): 98.6 min
- Revisit time (of the same area) on the equator: 12 days

- Both satellites:
- Period: 98.6 min / 2 = **‭49.3 min**
- Revisit time (of the same area): 12 days / 2 = 6 days

- info sources:
- https://www.esa.int/Applications/Observing_the_Earth/Copernicus/Sentinel-1/Facts_and_figures
- https://sentinel.esa.int/web/sentinel/missions/sentinel-1/satellite-description/orbit

## Sentinel-2

Consists of 2 satellites: Sentinel-2A, Sentinel-2B

- 1 satellite:
- Orbital cycle: 10 days
- Orbits per cycle: 143
- Calculated time for 1 orbit: ~100.699 min (should be the same as Period)
- Period (time to circle the Earth 1 time by = 1 orbit): 100.6 min
- Revisit time (of the same area) on the equator: 10 days

- Both satellites:
- Period: 100.6 min / 2 = **50.3 min**
- Revisit time (of the same area): 10 days / 2 = 5 days

- info sources:
- https://www.esa.int/Applications/Observing_the_Earth/Copernicus/Sentinel-2/Facts_and_figures
- https://sentinel.esa.int/web/sentinel/missions/sentinel-2/satellite-description/orbit

## Sentinel-3

Consists of 2 satellites: Sentinel-3A, Sentinel-3B

- 1 satellite:
- Orbital cycle: 27 days
- Orbits per cycle: 385
- Calculated time for 1 orbit: ~100.987 min (should be the same as Period)
- Period (time to circle the Earth 1 time = 1 orbit): 100.99 min
- Revisit time (of the same area) on the equator : SLSTR ~1 day, OLCI ~2 days

- Both satellites:
- Period: 100.99 min / 2 = **50.495 min**

- info sources:
- https://www.esa.int/Applications/Observing_the_Earth/Copernicus/Sentinel-3/Facts_and_figures
- https://sentinel.esa.int/web/sentinel/missions/sentinel-3/satellite-description/orbit


## Sentinel-5P

Consists of 1 satellite

- 1 satellite:
- Orbital cycle: 16 days
- Orbits per cycle: 227 (16 per day)
- Calculated time for 1 orbit: ~101.498 min (should be the same as Period)
- Period (time to circle the Earth 1 time = 1 orbit): **101 min**
- Revisit time (of the same area) on the equator: ???

- info sources:
- https://www.esa.int/Applications/Observing_the_Earth/Copernicus/Sentinel-5P/Facts_and_figures
- https://sentinel.esa.int/web/sentinel/missions/sentinel-5p/orbit
- https://www.esa.int/Enabling_Support/Operations/Sentinel-5P_operations

# Landsat

## Landsat 5

Consists of 1 satellite

- 1 satellite:
- Period (time to circle the Earth 1 time = 1 orbit): **99 min**
- Revisit time (of the same area) at the equator: 16 days

- info sources:
- https://www.usgs.gov/land-resources/nli/landsat/landsat-5

## Landsat 7

Consists of 1 satellite

- 1 satellite:
- Period (time to circle the Earth 1 time = 1 orbit): **99 min**
- Revisit time (of the same area) at the equator: 16 days

- info sources:
- https://www.usgs.gov/land-resources/nli/landsat/landsat-7


## Landsat 8

Consists of 1 satellite

- 1 satellite:
- Period (time to circle the Earth 1 time = 1 orbit): **99 min**
- Revisit time (of the same area) at the equator: 16 days

- info sources:
- https://www.usgs.gov/land-resources/nli/landsat/landsat-8

# Other

## MODIS

Consists of 1 satellite

- 1 satellite:
- Period (time to circle the Earth 1 time = 1 orbit): **99 min**

- info sources:
- https://sos.noaa.gov/datasets/polar-orbiting-aqua-satellite-and-modis-swath/

## DEM

- not applicable
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,14 +141,15 @@ We can always use layer to search for data availability:

const layerS2L2A = new S2L2ALayer(instanceId, 'S2L2A');
const cloudCoveragePercent = 50;
const tilesS2 = layerS2L2A.findTiles(bbox, fromDate, toDate, maxCount, offset, cloudCoverage);
const tilesS2L2A = layerS2L2A.findTiles(bbox, fromDate, toDate, maxCount, offset, cloudCoverage);
const flyoverIntervalsS2L2A = layerS2L2A.findFlyoverIntervals(tilesS2L2A.tiles);

const layerS1 = new S1GRDAWSEULayer(instanceId, 'LayerS1GRD');
const orbitDirection = OrbitDirection.ASCENDING;
const tilesS1 = layerS1.findTiles(bbox, fromDate, toDate, maxCount, offset, orbitDirection);
const flyoverIntervalsS1 = layerS1.findFlyoverIntervals(tilesS1.tiles);

const dates = layerS2L2A.findDatesUTC(bbox, fromDate, toDate, cloudCoverage);
const flyovers = layerS2L2A.findFlyovers(bbox, fromDate, toDate, cloudCoverage);
```

## Backwards compatibility
Expand Down
36 changes: 32 additions & 4 deletions example/node/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ require('dotenv').config({ path: '../../.env' });
const {
LayersFactory,
WmsLayer,
S1GRDIWAWSLayer,
S1GRDAWSEULayer,
S2L2ALayer,
OrbitDirection,
setAuthToken,
isAuthTokenSet,
requestAuthToken,
Expand Down Expand Up @@ -89,11 +90,11 @@ async function run() {

await setAuthTokenWithOAuthCredentials();

const layerS1 = new S1GRDIWAWSLayer(instanceId, s1grdLayerId);
const layerS1 = new S1GRDAWSEULayer(instanceId, s1grdLayerId);
printOut('Layer:', { layerId: layerS1.layerId, title: layerS1.title });
printOut('Orthorectify & backscatter:', { o: layerS1.orthorectify, b: layerS1.backscatterCoeff });

// finally, display the image:
// set the parameters for getting tiles, flyover intervals and images
const bbox = new BBox(CRS_EPSG4326, 18, 20, 20, 22);
printOut('BBox:', bbox);

Expand All @@ -107,9 +108,36 @@ async function run() {
};
printOut('GetMapParams:', getMapParams);

// get tiles and flyover intervals for S2 L2A layer
const layerS2L2A = new S2L2ALayer(instanceId, s2l2aLayerId);
const tilesS2L2A = await layerS2L2A.findTiles(
getMapParams.bbox,
getMapParams.fromTime,
getMapParams.toTime,
20,
0,
100,
);
printOut('tiles for S2 L2A:', tilesS2L2A);
const flyoverIntervalsS2L2A = layerS2L2A.findFlyoverIntervals(tilesS2L2A.tiles);
printOut('flyover intervals for S2 L2A:', flyoverIntervalsS2L2A);

// get tiles and flyover intervals for S1 GRD Layer
const tilesS1GRD = await layerS1.findTiles(
getMapParams.bbox,
getMapParams.fromTime,
getMapParams.toTime,
10,
0,
OrbitDirection.ASCENDING,
);
printOut('tiles for S1 GRD:', tilesS1GRD);
const flyoverIntervalsS1GRD = layerS1.findFlyoverIntervals(tilesS1GRD.tiles);
printOut('flyover intervals for S1 GRD:', flyoverIntervalsS1GRD);

// finally, display the image:
const imageUrl = await layerS2L2A.getMapUrl(getMapParams, ApiType.WMS);
printOut('URL:', imageUrl);
printOut('URL of S2 L2A image:', imageUrl);

// this doesn't work because node.js doesn't support Blob:
// const fs = require('fs');
Expand Down
38 changes: 37 additions & 1 deletion src/layer/AbstractLayer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import axios from 'axios';

import { GetMapParams, ApiType, PaginatedTiles } from 'src/layer/const';
import { GetMapParams, ApiType, Tile, PaginatedTiles, FlyoverInterval } from 'src/layer/const';
import { BBox } from 'src/bbox';
import { Dataset } from 'src/layer/dataset';

Expand Down Expand Up @@ -41,4 +41,40 @@ export class AbstractLayer {
): Promise<PaginatedTiles> {
throw new Error('Not implemented yet');
}

public findFlyoverIntervals(tiles: Tile[]): FlyoverInterval[] {
if (!this.dataset || !this.dataset.orbitTimeMinutes) {
throw new Error('Orbit time is needed for grouping tiles into flyovers.');
}

tiles.sort((a, b) => (new Date(a.sensingTime).getTime() > new Date(b.sensingTime).getTime() ? 1 : -1));
let orbitTimeMS = this.dataset.orbitTimeMinutes * 60 * 1000;
let flyoverIntervals: FlyoverInterval[] = [];

let j = 0;
for (let i = 0; i < tiles.length; i++) {
if (i === 0) {
flyoverIntervals[j] = {
fromTime: tiles[i].sensingTime,
toTime: tiles[i].sensingTime,
};
continue;
}

const prevDateMS = new Date(tiles[i - 1].sensingTime).getTime();
const currDateMS = new Date(tiles[i].sensingTime).getTime();
const diffMS = Math.abs(prevDateMS - currDateMS);

if (diffMS < orbitTimeMS) {
flyoverIntervals[j].toTime = tiles[i].sensingTime;
} else {
j++;
flyoverIntervals[j] = {
fromTime: tiles[i].sensingTime,
toTime: tiles[i].sensingTime,
};
}
}
return flyoverIntervals;
}
}
5 changes: 5 additions & 0 deletions src/layer/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ export type PaginatedTiles = {
hasMore: boolean;
};

export type FlyoverInterval = {
fromTime: string;
toTime: string;
};

export type MimeType =
| 'application/json'
| 'text/xml'
Expand Down
9 changes: 9 additions & 0 deletions src/layer/dataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type Dataset = {
shProcessingApiDatasourceAbbreviation: string;
shServiceHostname: string;
searchIndexUrl: string;
orbitTimeMinutes: number;
};

export const DATASET_AWSEU_S1GRD: Dataset = {
Expand All @@ -14,6 +15,7 @@ export const DATASET_AWSEU_S1GRD: Dataset = {
shProcessingApiDatasourceAbbreviation: 'S1GRD',
shServiceHostname: 'https://services.sentinel-hub.com/',
searchIndexUrl: 'https://services.sentinel-hub.com/index/v3/collections/S1GRD/searchIndex',
orbitTimeMinutes: 49.3,
};

export const DATASET_S2L2A: Dataset = {
Expand All @@ -23,6 +25,7 @@ export const DATASET_S2L2A: Dataset = {
shProcessingApiDatasourceAbbreviation: 'S2L2A',
shServiceHostname: 'https://services.sentinel-hub.com/',
searchIndexUrl: 'https://services.sentinel-hub.com/index/v3/collections/S2L2A/searchIndex',
orbitTimeMinutes: 50.3,
};

export const DATASET_S2L1C: Dataset = {
Expand All @@ -32,6 +35,7 @@ export const DATASET_S2L1C: Dataset = {
shProcessingApiDatasourceAbbreviation: 'S2L1C',
shServiceHostname: 'https://services.sentinel-hub.com/',
searchIndexUrl: 'https://services.sentinel-hub.com/index/v3/collections/S2L1C/searchIndex',
orbitTimeMinutes: 50.3,
};

export const DATASET_S3SLSTR: Dataset = {
Expand All @@ -41,6 +45,7 @@ export const DATASET_S3SLSTR: Dataset = {
shProcessingApiDatasourceAbbreviation: 'S3SLSTR',
shServiceHostname: 'https://creodias.sentinel-hub.com/',
searchIndexUrl: 'https://creodias.sentinel-hub.com/index/v3/collections/S3SLSTR/searchIndex',
orbitTimeMinutes: 50.495,
};

export const DATASET_S3OLCI: Dataset = {
Expand All @@ -50,6 +55,7 @@ export const DATASET_S3OLCI: Dataset = {
shProcessingApiDatasourceAbbreviation: 'S3OLCI',
shServiceHostname: 'https://creodias.sentinel-hub.com/',
searchIndexUrl: 'https://creodias.sentinel-hub.com/index/v3/collections/S3OLCI/searchIndex',
orbitTimeMinutes: 50.495,
};

export const DATASET_AWS_L8L1C: Dataset = {
Expand All @@ -59,6 +65,7 @@ export const DATASET_AWS_L8L1C: Dataset = {
shProcessingApiDatasourceAbbreviation: 'L8L1C',
shServiceHostname: 'https://services-uswest2.sentinel-hub.com/',
searchIndexUrl: 'https://services-uswest2.sentinel-hub.com/index/v3/collections/L8L1C/searchIndex',
orbitTimeMinutes: 99,
};

export const DATASET_MODIS: Dataset = {
Expand All @@ -68,6 +75,7 @@ export const DATASET_MODIS: Dataset = {
shProcessingApiDatasourceAbbreviation: 'MODIS',
shServiceHostname: 'https://services-uswest2.sentinel-hub.com/',
searchIndexUrl: 'https://services-uswest2.sentinel-hub.com/index/v3/collections/MODIS/searchIndex',
orbitTimeMinutes: 99,
};

export const DATASET_AWS_DEM: Dataset = {
Expand All @@ -77,4 +85,5 @@ export const DATASET_AWS_DEM: Dataset = {
shProcessingApiDatasourceAbbreviation: 'DEM',
shServiceHostname: 'https://services-uswest2.sentinel-hub.com/',
searchIndexUrl: null,
orbitTimeMinutes: null,
};

0 comments on commit 53320b4

Please sign in to comment.