Sentinel Hub OGC services

Disclaimer:

Sentinel Hub service has a dedicated Process API that extends the limiting capabilities of the standard OGC endpoints. We advise you to move to Process API and enjoy the full power of Sentinel Hub services. See the notebook with Process API examples.

Web Map Service (WMS) and Web Coverage Service (WCS)

In this example notebook we show how to use WMS and WCS services provided by Sentinel Hub to download satellite imagery. We describe how to use various parameters and configurations to obtain either processed products or raw band data.

We start with examples using Sentinel-2 L1C data and then show how to also obtain Sentinel-2 L2A, Sentinel-1, Landsat 8, MODIS and DEM data.

Prerequisites

For this functionality Sentinel Hub instance ID has to be configured according to the instructions.

[1]:
from sentinelhub import SHConfig

config = SHConfig()

if config.instance_id == "":
    print("Warning! To use OGC functionality of Sentinel Hub, please configure the `instance_id`.")

Imports

[2]:
%reload_ext autoreload
%autoreload 2
%matplotlib inline
[3]:
import datetime

import matplotlib.pyplot as plt
import numpy as np

Note: matplotlib is not a dependency of sentinelhub.

[4]:
from sentinelhub import CRS, BBox, DataCollection, MimeType, WcsRequest, WmsRequest
[5]:
def plot_image(image, factor=1):
    """
    Utility function for plotting RGB images.
    """
    plt.subplots(nrows=1, ncols=1, figsize=(15, 7))

    if np.issubdtype(image.dtype, np.floating):
        plt.imshow(np.minimum(image * factor, 1))
    else:
        plt.imshow(image)

Setting area of interest

We will download Sentinel-2 imagery of Betsiboka Estuary such as the one shown below (taken by Sentinel-2 on 2017-12-15):
title

The bounding box in WGS84 coordinate system is (longitude and latitude coordinates of upper left and lower right corners):

[6]:
betsiboka_coords_wgs84 = (46.16, -16.15, 46.51, -15.58)

All requests require bounding box to be given as an instance of sentinelhub.geometry.BBox with corresponding Coordinate Reference System (sentinelhub.geometry.CRS). In our case it is in WGS84 and we can use the predefined WGS84 coordinate reference system from sentinelhub.geometry.CRS.

[7]:
betsiboka_bbox = BBox(bbox=betsiboka_coords_wgs84, crs=CRS.WGS84)

WMS request

Example 1: True color (PNG) on a specific date

We need to specify the following arguments in the initialization of a WmsRequest:

  • layer - set it to 'TRUE-COLOR-S2-L1C'

    • In case you are not using a configuration based on Python scripts template you will now have to create a layer named TRUE-COLOR-S2-L1C yourself. In Sentinel Hub Dashboard go to your configuration, add new layer which will use Sentinel-2 L1C data collection and predefined product TRUE COLOR, RGB Visualization for Data processing parameter.

  • bbox - see above

  • time - acquisition date

    • we’ll set it to 2017-12-15

  • width and height - width and height of a returned image

    • we’ll set them to 512 and 856, respectively

    • we could only set one of the two parameters and the other one would be set automatically in a way that image would best fit bounding box ratio

  • instance_id - see above

All of the above arguments are obligatory and have to be set for all WmsRequest.

[8]:
wms_true_color_request = WmsRequest(
    data_collection=DataCollection.SENTINEL2_L1C,
    layer="TRUE-COLOR-S2-L1C",
    bbox=betsiboka_bbox,
    time="2017-12-15",
    width=512,
    height=856,
    config=config,
)
[9]:
wms_true_color_img = wms_true_color_request.get_data()

Method get_data() will always return a list images in form of numpy arrays.

[10]:
print("Returned data is of type = %s and length %d." % (type(wms_true_color_img), len(wms_true_color_img)))
Returned data is of type = <class 'list'> and length 1.
[11]:
print(
    "Single element in the list is of type {} and has shape {}".format(
        type(wms_true_color_img[-1]), wms_true_color_img[-1].shape
    )
)
Single element in the list is of type <class 'numpy.ndarray'> and has shape (856, 512, 4)
[12]:
plot_image(wms_true_color_img[-1])
../_images/examples_ogc_request_25_0.png

Example 2: True color of the latest acquisition

In order to get the latest Sentinel-2 acquisition set the time argument to 'latest'.

[13]:
wms_true_color_request = WmsRequest(
    data_collection=DataCollection.SENTINEL2_L1C,
    layer="TRUE-COLOR-S2-L1C",
    bbox=betsiboka_bbox,
    time="latest",
    width=512,
    height=856,
    config=config,
)
[14]:
wms_true_color_img = wms_true_color_request.get_data()
[15]:
plot_image(wms_true_color_img[-1])
../_images/examples_ogc_request_29_0.png
[16]:
print("The latest Sentinel-2 image of this area was taken on {}.".format(wms_true_color_request.get_dates()[-1]))
The latest Sentinel-2 image of this area was taken on 2021-02-27 07:14:05.

In case a part of the image above is completely white that is because the latest acquisition only partially intersected the specified bounding box. To avoid that we could use a time_difference parameter described in Example 8.

Example 3: True color of the multiple acquisitions in certain time window

In order to get all Sentinel-2 acquisitions taken in a certain time interval set the time argument to tuple with two elements (start date,end date).

[17]:
wms_true_color_request = WmsRequest(
    data_collection=DataCollection.SENTINEL2_L1C,
    layer="TRUE-COLOR-S2-L1C",
    bbox=betsiboka_bbox,
    time=("2017-12-01", "2017-12-31"),
    width=512,
    height=856,
    config=config,
)
[18]:
wms_true_color_img = wms_true_color_request.get_data()
[19]:
print("There are %d Sentinel-2 images available for December 2017." % len(wms_true_color_img))
There are 6 Sentinel-2 images available for December 2017.
[20]:
plot_image(wms_true_color_img[2])
../_images/examples_ogc_request_36_0.png
[21]:
print("These %d images were taken on the following dates:" % len(wms_true_color_img))
for index, date in enumerate(wms_true_color_request.get_dates()):
    print(" - image %d was taken on %s" % (index, date))
These 6 images were taken on the following dates:
 - image 0 was taken on 2017-12-05 07:13:30
 - image 1 was taken on 2017-12-10 07:12:10
 - image 2 was taken on 2017-12-15 07:12:03
 - image 3 was taken on 2017-12-20 07:12:10
 - image 4 was taken on 2017-12-25 07:12:04
 - image 5 was taken on 2017-12-30 07:12:09

Example 4: True color of the multiple acquisitions in certain time window with cloud coverage less than 30%

In order to get only Sentinel-2 acquisitions with cloud coverage less than certain amount set maxcc argument to that value. Note that this cloud coverage is estimated on the entire Sentinel-2 tile and not just for the region defined by our bounding box.

[22]:
wms_true_color_request = WmsRequest(
    data_collection=DataCollection.SENTINEL2_L1C,
    layer="TRUE-COLOR-S2-L1C",
    bbox=betsiboka_bbox,
    time=("2017-12-01", "2017-12-31"),
    width=512,
    height=856,
    maxcc=0.3,
    config=config,
)
[23]:
wms_true_color_img = wms_true_color_request.get_data()
[24]:
print(
    "There are %d Sentinel-2 images available for December 2017 with cloud coverage less than %1.0f%%."
    % (len(wms_true_color_img), wms_true_color_request.maxcc * 100.0)
)
There are 2 Sentinel-2 images available for December 2017 with cloud coverage less than 30%.
[25]:
plot_image(wms_true_color_img[-1])
../_images/examples_ogc_request_42_0.png
[26]:
print("These %d images were taken on the following dates:" % len(wms_true_color_img))
for index, date in enumerate(wms_true_color_request.get_dates()):
    print(" - image %d was taken on %s" % (index, date))
These 2 images were taken on the following dates:
 - image 0 was taken on 2017-12-15 07:12:03
 - image 1 was taken on 2017-12-20 07:12:10

Example 5: All Sentinel-2’s raw band values

Now let’s use a layer named BANDS-S2-L1C which will return all Sentinel-2 spectral bands with raw values.

If you are not using a configuration based on Python scripts template you will again have to create such layer manually. In that case define Data processing parameter with the following custom script:

return [B01,B02,B03,B04,B05,B06,B07,B08,B8A,B09,B10,B11,B12]

We have to set the image_format argument to sentinelhub.constants.MimeType.TIFF, since we can’t pack all Sentinel-2’s 13 bands into a png image. A type of returned data (uint8, uint16 or float32) should be set in an layer definition in the Dashboard.

[27]:
wms_bands_request = WmsRequest(
    data_collection=DataCollection.SENTINEL2_L1C,
    layer="BANDS-S2-L1C",
    bbox=betsiboka_bbox,
    time="2017-12-15",
    width=512,
    height=856,
    image_format=MimeType.TIFF,
    config=config,
)

wms_bands_img = wms_bands_request.get_data()
[28]:
wms_bands_img[-1][:, :, 12].shape
[28]:
(856, 512)

Image showing SWIR band B12

[29]:
plot_image(wms_bands_img[-1][:, :, 12])
../_images/examples_ogc_request_48_0.png

From raw bands we can also construct a true color image

[30]:
plot_image(wms_bands_img[-1][:, :, [3, 2, 1]], 2.5)
../_images/examples_ogc_request_50_0.png

Last band is `dataMask <https://docs.sentinel-hub.com/api/latest/data/sentinel-2-l1c/#available-bands-and-data>`__, in this case showing we have data over the whole image.

[31]:
plt.imshow(wms_bands_img[-1][:, :, -1], vmin=0, vmax=1);
../_images/examples_ogc_request_52_0.png

Example 6: Save downloaded data to disk and read it from disk

All downloaded data can be saved to disk and later read from it. Simply specify the location on disk where data should be saved (or loaded from) via data_folder argument of request’s constructor and set the argument save_data of get_data method to True.

[32]:
wms_bands_request = WmsRequest(
    data_collection=DataCollection.SENTINEL2_L1C,
    data_folder="test_dir",
    layer="BANDS-S2-L1C",
    bbox=betsiboka_bbox,
    time="2017-12-15",
    width=512,
    height=856,
    image_format=MimeType.TIFF,
    config=config,
)
[33]:
%%time
wms_bands_img = wms_bands_request.get_data(save_data=True)
CPU times: user 200 ms, sys: 54.9 ms, total: 255 ms
Wall time: 5.52 s

The output directory has been created and a tiff file with all 13 bands was saved into the following structure:

[34]:
import os

for folder, _, filenames in os.walk(wms_bands_request.data_folder):
    for filename in filenames:
        print(os.path.join(folder, filename))
test_dir/c7cf5a2660a65f3dc8e98e5deddb80d4/request.json
test_dir/c7cf5a2660a65f3dc8e98e5deddb80d4/response.tiff

Since data has been already downloaded the next request will read the data from disk instead of downloading it. That will be much faster.

[35]:
wms_bands_request_from_disk = WmsRequest(
    data_collection=DataCollection.SENTINEL2_L1C,
    data_folder="test_dir",
    layer="BANDS-S2-L1C",
    bbox=betsiboka_bbox,
    time="2017-12-15",
    width=512,
    height=856,
    image_format=MimeType.TIFF,
    config=config,
)
[36]:
%%time

wms_bands_img_from_disk = wms_bands_request_from_disk.get_data()
CPU times: user 132 ms, sys: 7.28 ms, total: 140 ms
Wall time: 73.7 ms
[37]:
if np.array_equal(wms_bands_img[-1], wms_bands_img_from_disk[-1]):
    print("Arrays are equal.")
else:
    print("Arrays are different.")
Arrays are equal.

If you need to redownload the data again, just set the redownload argument of get_data() method to True.

[38]:
%%time
wms_bands_img_redownload = wms_bands_request_from_disk.get_data(redownload=True)
CPU times: user 200 ms, sys: 50.4 ms, total: 250 ms
Wall time: 5.2 s

Example 7: Save downloaded data directly to disk

The get_data method returns a list of numpy arrays and can save the downloaded data to disk, as we have seen in the previous example. Sometimes you would just like to save the data directly to disk for later use. You can do that by using save_data method instead.

[39]:
wms_true_color_request = WmsRequest(
    data_collection=DataCollection.SENTINEL2_L1C,
    data_folder="test_dir_tiff",
    layer="TRUE-COLOR-S2-L1C",
    bbox=betsiboka_bbox,
    time=("2017-12-01", "2017-12-31"),
    width=512,
    height=856,
    image_format=MimeType.TIFF,
    config=config,
)
[40]:
%%time
wms_true_color_request.save_data()
CPU times: user 144 ms, sys: 54.2 ms, total: 198 ms
Wall time: 1.41 s

The output directory has been created and tiff files for all 6 images should be in it.

[41]:
os.listdir(wms_true_color_request.data_folder)
[41]:
['a09c4e3815f2015d4aa7cc024063e5ae',
 '8686ac3a1da32e8a53cee1944683cb63',
 '2a536a80f9d164407360c13e9a320429',
 'f8c92a9284588890ab3e29f17c554ef9',
 '3f8cb8a6f9a1e67099759d685679ff4a',
 'b94bf6ab0816498336737525eacabe94']

Example 8: Merging two or more download requests into one

If the bounding box spans over two or more Sentinel-2 tiles and each of them has slightly different time stamp, then download request will be created for each time stamp. Therefore we will obtain two or more images which could be completely the same or partially blank. It depends on whether the tiles from the same orbit are from the same or from two different data strips.

Let’s look at the specific example. Again, we’re going to look at Betsiboka estuary, but we’ll increase the bounding box so that we cover an area of two different Senteinel-2 tiles.

[42]:
betsiboka_bbox_large = BBox((45.88, -16.12, 47.29, -15.45), crs=CRS.WGS84)

wms_true_color_request = WmsRequest(
    data_collection=DataCollection.SENTINEL2_L1C,
    layer="TRUE-COLOR-S2-L1C",
    bbox=betsiboka_bbox_large,
    time="2015-12-01",
    width=960,
    image_format=MimeType.PNG,
    config=config,
)

wms_true_color_img = wms_true_color_request.get_data()
[43]:
plot_image(wms_true_color_img[0])
plot_image(wms_true_color_img[1])
../_images/examples_ogc_request_71_0.png
../_images/examples_ogc_request_71_1.png

Clearly these are the same images and we usually would want to get only one. We can do that by widening the time interval in which two or more download requests are considered to be the same. In our example it is enough to widen the time window for 10 minutes, but usually it can be up to two hours. This is done by setting the time_difference argument.

[44]:
wms_true_color_request_with_deltat = WmsRequest(
    data_collection=DataCollection.SENTINEL2_L1C,
    layer="TRUE-COLOR-S2-L1C",
    bbox=betsiboka_bbox_large,
    time="2015-12-01",
    width=960,
    image_format=MimeType.PNG,
    time_difference=datetime.timedelta(hours=2),
    config=config,
)

wms_true_color_img = wms_true_color_request_with_deltat.get_data()
[45]:
print("These %d images were taken on the following dates:" % len(wms_true_color_img))
for index, date in enumerate(wms_true_color_request_with_deltat.get_dates()):
    print(" - image %d was taken on %s" % (index, date))
These 1 images were taken on the following dates:
 - image 0 was taken on 2015-12-01 07:12:50
[46]:
plot_image(wms_true_color_img[-1])
../_images/examples_ogc_request_75_0.png

WCS request

The use of WcsRequest is exactly the same as of the WmsRequest shown above. The only difference is that instead of specifying image size we specify the spatial resolution of the image. We do that by setting the resx and resy arguments to the desired resolution in meters. E.g. setting resx='10m' and resy='10m' will return an image where every pixel will cover an area of size 10m x 10m.

Every other parameter described in this tutorial will work the same for WMS and WCS requests.

Example 9: True color with specified resolution

[47]:
wcs_true_color_request = WcsRequest(
    data_collection=DataCollection.SENTINEL2_L1C,
    layer="TRUE-COLOR-S2-L1C",
    bbox=betsiboka_bbox,
    time="2017-12-15",
    resx="60m",
    resy="60m",
    config=config,
)

wcs_true_color_img = wcs_true_color_request.get_data()
[48]:
print(
    "Single element in the list is of type = {} and has shape {}".format(
        type(wcs_true_color_img[-1]), wcs_true_color_img[-1].shape
    )
)
Single element in the list is of type = <class 'numpy.ndarray'> and has shape (1057, 624, 4)
[49]:
plot_image(wcs_true_color_img[-1])
../_images/examples_ogc_request_80_0.png

Custom URL Parameters

Sentinel Hub OGC services have various custom URL parameters described at the webpage. Many of them are supported in this package and some of them might be added in the future. Let’s check which ones currently exist.

[50]:
from sentinelhub import CustomUrlParam

list(CustomUrlParam)
[50]:
[<CustomUrlParam.SHOWLOGO: 'ShowLogo'>,
 <CustomUrlParam.EVALSCRIPT: 'EvalScript'>,
 <CustomUrlParam.EVALSCRIPTURL: 'EvalScriptUrl'>,
 <CustomUrlParam.PREVIEW: 'Preview'>,
 <CustomUrlParam.QUALITY: 'Quality'>,
 <CustomUrlParam.UPSAMPLING: 'Upsampling'>,
 <CustomUrlParam.DOWNSAMPLING: 'Downsampling'>,
 <CustomUrlParam.GEOMETRY: 'Geometry'>,
 <CustomUrlParam.MINQA: 'MinQA'>]

Many of these parameters already appear in Sentinel Hub Dashboard as a property of an instance or a layer. However any parameter we specify in the code will automatically override the definition in Dashboard for our request.

Example 10: Using Custom URL Parameters

We can request true color image without SentinelHub logo.

[51]:
custom_wms_request = WmsRequest(
    data_collection=DataCollection.SENTINEL2_L1C,
    layer="TRUE-COLOR-S2-L1C",
    bbox=betsiboka_bbox,
    time="2019-11-05",
    width=512,
    height=856,
    custom_url_params={CustomUrlParam.SHOWLOGO: True},
    config=config,
)

custom_wms_data = custom_wms_request.get_data()

Obtained true color images have dataMask channel (defined as an output of the TRUE-COLOR-S2-L1C layer) indicating which parts of the image have no data. SentinelHub logo is visible at the bottom of the image.

[52]:
plot_image(custom_wms_data[-1][:, :, :3])
plot_image(custom_wms_data[-1][:, :, 3])
../_images/examples_ogc_request_86_0.png
../_images/examples_ogc_request_86_1.png

Example 11: Evalscript

Instead of using Sentinel Hub Dashboard we can define our own custom layers inside Python with CustomUrlParam.EVALSCRIPT. All we need is a chunk of code written in Javascript which is not too long to fit into a URL.

Let’s implement a simple cloud detection algorithm.

[53]:
# by Braaten, Cohen, Yang 2015
my_evalscript = """
var bRatio = (B01 - 0.175) / (0.39 - 0.175);
var NGDR = (B01 - B02) / (B01 + B02);

function clip(a) {
    return a>0 ? (a<1 ? a : 1) : 0;
}

if (bRatio > 1) {
    var v = 0.5*(bRatio - 1);
    return [0.5*clip(B04), 0.5*clip(B03), 0.5*clip(B02) + v];
}

if (bRatio > 0 && NGDR > 0) {
    var v = 5 * Math.sqrt(bRatio * NGDR);
    return [0.5 * clip(B04) + v, 0.5 * clip(B03), 0.5 * clip(B02)];
}

return [2*B04, 2*B03, 2*B02];
"""

evalscript_wms_request = WmsRequest(
    data_collection=DataCollection.SENTINEL2_L1C,
    layer="TRUE-COLOR-S2-L1C",  # Layer parameter can be any existing Sentinel-2 L1C layer
    bbox=betsiboka_bbox,
    time="2017-12-20",
    width=512,
    custom_url_params={CustomUrlParam.EVALSCRIPT: my_evalscript},
    config=config,
)

evalscript_wms_data = evalscript_wms_request.get_data()
plot_image(evalscript_wms_data[0])
../_images/examples_ogc_request_88_0.png

Note: We still had to specify an existing layer from Dashboard. That is because each layer is linked with it’s data collection and we cannot override layer’s data collection from the code.

Example 12: Evalscript URL

Another option is to simply provide a URL address of an evalscript written in Javascript. For that purpose we have created a collection of useful custom scripts on Github.

Let’s select a script for calculating moisture index and provide its URL as a value of parameter CustomUrlParam.EVALSCRIPTURL.

[54]:
my_url = "https://raw.githubusercontent.com/sentinel-hub/custom-scripts/master/sentinel-2/ndmi_special/script.js"

evalscripturl_wms_request = WmsRequest(
    data_collection=DataCollection.SENTINEL2_L1C,
    layer="TRUE-COLOR-S2-L1C",  # Layer parameter can be any existing Sentinel-2 L1C layer
    bbox=betsiboka_bbox,
    time="2017-12-20",
    width=512,
    custom_url_params={CustomUrlParam.EVALSCRIPTURL: my_url},
    config=config,
)

evalscripturl_wms_data = evalscripturl_wms_request.get_data()
plot_image(evalscripturl_wms_data[0])
../_images/examples_ogc_request_90_0.png

Data Collections

The package supports various data collections. Default data collection is Sentinel-2 L1C however currently the following is supported:

[55]:
for collection in DataCollection.get_available_collections():
    print(collection)
DataCollection.SENTINEL2_L1C
DataCollection.SENTINEL2_L2A
DataCollection.SENTINEL1
DataCollection.SENTINEL1_IW
DataCollection.SENTINEL1_IW_ASC
DataCollection.SENTINEL1_IW_DES
DataCollection.SENTINEL1_EW
DataCollection.SENTINEL1_EW_ASC
DataCollection.SENTINEL1_EW_DES
DataCollection.SENTINEL1_EW_SH
DataCollection.SENTINEL1_EW_SH_ASC
DataCollection.SENTINEL1_EW_SH_DES
DataCollection.DEM
DataCollection.MODIS
DataCollection.LANDSAT_MSS_L1
DataCollection.LANDSAT_TM_L1
DataCollection.LANDSAT_TM_L2
DataCollection.LANDSAT_ETM_L1
DataCollection.LANDSAT_ETM_L2
DataCollection.LANDSAT_OT_L1
DataCollection.LANDSAT_OT_L2
DataCollection.SENTINEL5P
DataCollection.SENTINEL3_OLCI
DataCollection.SENTINEL3_SLSTR

In order to obtain data from any of these data collections with WmsRequest or WcsRequest we have to do the following:

  • Use a configuration based on Python scripts template or create a new layer in Sentinel Hub Dashboard that is defined to use desired satellite data collection. Set the layer parameter of WmsRequest or WcsRequest to the name of this newly created layer.

  • Set data_collection parameter of WmsRequest or WcsRequest to the same data collection (using one of the objects from the list above).

Example 13: Sentinel-2 L2A

When you have a layer named TRUE-COLOR-S2-L2A in your configuration let’s try to obtain some level 2A images. Unfortunately L2A images are being processed only for some regions around the globe and Betsiboka Estuary is not one of them.

Instead let’s check Eyjafjallajökull volcano on Iceland. This time we will provide coordinates in Popular Web Mercator CRS.

[56]:
volcano_bbox = BBox(bbox=(-2217485.0, 9228907.0, -2150692.0, 9284045.0), crs=CRS.POP_WEB)

l2a_request = WmsRequest(
    data_collection=DataCollection.SENTINEL2_L2A,
    layer="TRUE-COLOR-S2-L2A",
    bbox=volcano_bbox,
    time="2017-08-30",
    width=512,
    config=config,
)

l2a_data = l2a_request.get_data()
plot_image(l2a_data[0])
../_images/examples_ogc_request_95_0.png

Example 14: DEM

Request using Mapzen DEM as a data collection does not require a time parameter.

[57]:
dem_request = WmsRequest(
    data_collection=DataCollection.DEM,
    layer="DEM",
    bbox=volcano_bbox,
    width=512,
    image_format=MimeType.TIFF,
    custom_url_params={CustomUrlParam.SHOWLOGO: False},
    config=config,
)

dem_image = dem_request.get_data()[0]

# Taking the first channel because the second channel is a valid data mask
dem_values = dem_image[..., 0]

plot_image(dem_values, 1 / np.amax(dem_values))
../_images/examples_ogc_request_97_0.png

Example 15: Landsat 8

To view Landsat 8 L1C image we require a layer TRUE-COLOR-L8 with predefined true color RGB template.

[58]:
l8_request = WmsRequest(
    data_collection=DataCollection.LANDSAT_OT_L1,
    layer="TRUE-COLOR-L8",
    bbox=volcano_bbox,
    time="2017-08-20",
    width=512,
    config=config,
)

l8_data = l8_request.get_data()
plot_image(l8_data[-1])
../_images/examples_ogc_request_99_0.png

Example 15: Sentinel-1

If we would like to avoid clouds using Sentinel-1 radar data seems like a good idea. The package supports obtaining multiple types of Sentinel-1 data. In this example we will use DataCollection.SENTINEL1_IW. While creating layer in Sentinel Hub Dashboard we must be careful to use the same settings as the supported data collection:

[59]:
DataCollection.SENTINEL1_IW
[59]:
<DataCollection.SENTINEL1_IW: DataCollectionDefinition(
  api_id: S1GRD
  catalog_id: sentinel-1-grd
  wfs_id: DSS3
  collection_type: Sentinel-1
  sensor_type: C-SAR
  processing_level: GRD
  swath_mode: IW
  polarization: DV
  resolution: HIGH
  orbit_direction: BOTH
  bands: ('VV', 'VH')
  is_timeless: False
)>

This tells us we have to set acquisition parameter to IW, polarisation to DV, resolution to High and orbit direction to Both. After that let’s name the layer TRUE-COLOR-S1-IW and use the following custom script

return [VV, 2 * VH, VV / VH / 100.0]
[60]:
s1_request = WmsRequest(
    data_collection=DataCollection.SENTINEL1_IW,
    layer="TRUE-COLOR-S1-IW",
    bbox=volcano_bbox,
    time="2017-10-03",
    width=512,
    config=config,
)

s1_data = s1_request.get_data()
plot_image(s1_data[-1])
../_images/examples_ogc_request_103_0.png

Example 16: Sentinel-1, ascending orbit direction

Sentinel-1 data is acquired when a satellite travels either from approx. north to south (i.e. DESCENDING orbit direction) or from approx. south to north (i.e.ASCENDING orbit direction). With sentinelhub-py package one can request only the data with ASCENDING orbit direction if using a data collection ending with “_ASC” (or only data with DESCENDING orbit direction when using data collection ending with “_DES”).

E.g., the request below will fetch only the data with ASCENDING orbit direction:

[61]:
s1_asc_request = WmsRequest(
    data_collection=DataCollection.SENTINEL1_IW_ASC,
    layer="TRUE-COLOR-S1-IW",
    bbox=volcano_bbox,
    time=("2017-10-03", "2017-10-05"),
    width=512,
    config=config,
)

s1_asc_data = s1_asc_request.get_data()
plot_image(s1_asc_data[-1])
../_images/examples_ogc_request_106_0.png

Why is left side of the image black? Let’s have a look at dataMask. Unfortunately, TRUE-COLOR-S1-IW layer is not defined to return dataMask, but we can always use evalscript…

[62]:
s1_with_datamask_evalscript = """
//VERSION=3

function evaluatePixel(sample) {
    return [sample.VV, 2 * sample.VH, sample.VV / sample.VH / 100.0, sample.dataMask]
}

function setup() {
  return {
    input: [{
      bands: [
        "VV",
        "VH",
        "dataMask"
      ]
    }],
    output: {
      bands: 4,
      sampleType:"FLOAT32"
    }
  }
}
"""
s1_asc_request_datamask = WmsRequest(
    data_collection=DataCollection.SENTINEL1_IW_ASC,
    layer="TRUE-COLOR-S1-IW",
    bbox=volcano_bbox,
    time=("2017-10-03", "2017-10-05"),
    width=512,
    image_format=MimeType.TIFF,
    custom_url_params={CustomUrlParam.EVALSCRIPT: s1_with_datamask_evalscript},
    config=config,
)

s1_asc_request_datamask_data = s1_asc_request_datamask.get_data()
plot_image(s1_asc_request_datamask_data[-1])
plot_image(s1_asc_request_datamask_data[-1][:, :, -1])
../_images/examples_ogc_request_108_0.png
../_images/examples_ogc_request_108_1.png

Example 17: Custom (BYOC) data

It is possible that you “bring your own data” (BYOC) and access it using Sentinel Hub in a similar manner as any other satellite data. To be able to access your own data using Sentinel Hub you will need to prepare a few things. Roughly speaking these are (find all details here: https://docs.sentinel-hub.com/api/latest/#/API/byoc): - Convert your data to Cloud Optimized GeoTiff (COG) format. Store it in AWS S3 bucket and allow SH to access it. - Create collection in SH, which points to the S3 bucket. Within the collection, create tiles. (This step is possible also with SentinelHub.py library, using SentinelHubBYOC class.) - Create new layer in your configuration, which points to the collection. - Request the data using SentinelHub.py package

Although requesting BYOC data works with SentinelHub OGC services, the Process API allows much better tools to work with your data. For one thing, you do not have to predefine any layers, but can work with (multi-temporal) evalscripts directly. Nevertheless, the following code snippet will work if you specify your BYOC collection_id and a layer configuration, defined in Sentinel Hub dashboard to use the BYOC collection.

[67]:
byoc_bbox = BBox((13.82387, 45.85221, 13.83313, 45.85901), crs=CRS.WGS84)

collection_id = "<your BYOC collection id>"
layer = "<layer that uses the BYOC collection, defined in SentinelHub dashboard>"

byoc_request = WmsRequest(
    data_collection=DataCollection.define_byoc(collection_id), layer=layer, bbox=byoc_bbox, width=512, config=config
)

byoc_data = byoc_request.get_data()
plot_image(byoc_data[-1])