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

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 productTRUE COLOR, RGB Visualization
forData processing
parameter.
bbox
- see abovetime
- acquisition datewe’ll set it to 2017-12-15
width
andheight
- width and height of a returned imagewe’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])

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])

[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])

[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])

[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])

From raw bands we can also construct a true color image
[30]:
plot_image(wms_bands_img[-1][:, :, [3, 2, 1]], 2.5)

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);

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])


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])

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])

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])


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])

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])

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 ofWmsRequest
orWcsRequest
to the name of this newly created layer.Set
data_collection
parameter ofWmsRequest
orWcsRequest
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])

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))

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])

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])

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])

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])


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])