Using Python to search and download Sentinel data¶
from sentinelsat import SentinelAPI, read_geojson, geojson_to_wkt
import xarray
import pandas as pd
“Sentinelsat makes searching, downloading and retrieving the metadata of Sentinel satellite images from the Copernicus Open Access Hub easy.” From Sentinelsat — Sentinelsat 1.1.1 documentation
- connect to the Python API
api = SentinelAPI('username', 'password', 'https://apihub.copernicus.eu/apihub')
- search by polygon, time, and SciHub query keywords
I’ve created a simple ROI (rectangle) as geojson file, called map.geojson in order to intersect the footprint with data to search.
footprint = geojson_to_wkt(read_geojson('../sentinel1/map.geojson'))
products = api.query(footprint,
date = ('20180605', '20180610'),
platformname = 'Sentinel-1',
producttype='SLC')
A SentinelAPI object can allow different parameters. Here it is date of ingestion , the type of platform and wihich kkind of product you need.
- convert to Pandas DataFrame
products_df = api.to_dataframe(products)
products_df
Testing products_df visualization as xarray¶
products_df.to_xarray()
<xarray.Dataset> Dimensions: (index: 1) Coordinates: * index (index) object 'b5c7ca6d-768e-4600-a2dd-556d... Data variables: (12/42) title (index) object 'S2B_MSIL2A_20220430T100019_N... link (index) object "https://apihub.copernicus.eu... link_alternative (index) object "https://apihub.copernicus.eu... link_icon (index) object "https://apihub.copernicus.eu... summary (index) object 'Date: 2022-04-30T10:00:19.02... ondemand (index) object 'false' ... ... platformserialidentifier (index) object 'Sentinel-2B' processinglevel (index) object 'Level-2A' datastripidentifier (index) object 'S2B_OPER_MSI_L2A_DS_2BPS_202... granuleidentifier (index) object 'S2B_OPER_MSI_L2A_TL_2BPS_202... identifier (index) object 'S2B_MSIL2A_20220430T100019_N... uuid (index) object 'b5c7ca6d-768e-4600-a2dd-556d...
- index: 1
- index(index)object'b5c7ca6d-768e-4600-a2dd-556d31b…
array(['b5c7ca6d-768e-4600-a2dd-556d31b05693'], dtype=object)
- title(index)object'S2B_MSIL2A_20220430T100019_N040…
array(['S2B_MSIL2A_20220430T100019_N0400_R122_T33TVF_20220430T131131'], dtype=object)
- link(index)object"https://apihub.copernicus.eu/ap…
array(["https://apihub.copernicus.eu/apihub/odata/v1/Products('b5c7ca6d-768e-4600-a2dd-556d31b05693')/$value"], dtype=object)
- link_alternative(index)object"https://apihub.copernicus.eu/ap…
array(["https://apihub.copernicus.eu/apihub/odata/v1/Products('b5c7ca6d-768e-4600-a2dd-556d31b05693')/"], dtype=object)
- link_icon(index)object"https://apihub.copernicus.eu/ap…
array(["https://apihub.copernicus.eu/apihub/odata/v1/Products('b5c7ca6d-768e-4600-a2dd-556d31b05693')/Products('Quicklook')/$value"], dtype=object)
- summary(index)object'Date: 2022-04-30T10:00:19.024Z,…
array(['Date: 2022-04-30T10:00:19.024Z, Instrument: MSI, Satellite: Sentinel-2, Size: 624.78 MB'], dtype=object)
- ondemand(index)object'false'
array(['false'], dtype=object)
- generationdate(index)datetime64[ns]2022-04-30T13:11:31
array(['2022-04-30T13:11:31.000000000'], dtype='datetime64[ns]')
- beginposition(index)datetime64[ns]2022-04-30T10:00:19.024000
array(['2022-04-30T10:00:19.024000000'], dtype='datetime64[ns]')
- endposition(index)datetime64[ns]2022-04-30T10:00:19.024000
array(['2022-04-30T10:00:19.024000000'], dtype='datetime64[ns]')
- ingestiondate(index)datetime64[ns]2022-04-30T18:31:42.412000
array(['2022-04-30T18:31:42.412000000'], dtype='datetime64[ns]')
- orbitnumber(index)int6426889
array([26889], dtype=int64)
- relativeorbitnumber(index)int64122
array([122], dtype=int64)
- illuminationazimuthangle(index)float64153.8
array([153.80771058])
- illuminationzenithangle(index)float6428.4
array([28.39702244])
- vegetationpercentage(index)float6448.8
array([48.802644])
- notvegetatedpercentage(index)float6414.68
array([14.678153])
- waterpercentage(index)float6427.68
array([27.681303])
- unclassifiedpercentage(index)float640.1426
array([0.142617])
- mediumprobacloudspercentage(index)float642.283
array([2.282767])
- highprobacloudspercentage(index)float641.624
array([1.623816])
- snowicepercentage(index)float640.007217
array([0.007217])
- cloudcoverpercentage(index)float646.358
array([6.358208])
- level1cpdiidentifier(index)object'S2B_OPER_MSI_L1C_TL_2BPS_202204…
array(['S2B_OPER_MSI_L1C_TL_2BPS_20220430T120209_A026889_T33TVF_N04.00'], dtype=object)
- gmlfootprint(index)object'<gml:Polygon srsName="http://ww…
array(['<gml:Polygon srsName="http://www.opengis.net/gml/srs/epsg.xml#4326" xmlns:gml="http://www.opengis.net/gml">\n <gml:outerBoundaryIs>\n <gml:LinearRing>\n <gml:coordinates>41.54970986158542,14.676121401599385 41.475316413364425,14.648566945908971 41.39209831941913,14.617403784322985 41.328546882077774,14.593549486819448 41.18196503364706,14.54105211414984 41.07059614201777,14.500741494562956 41.036462304690495,14.48834005349154 40.90252914944609,14.439669454929737 40.88941849252913,14.434900408922116 40.74322865985612,14.381979877189965 40.597110577059766,14.328944144185272 40.55900004804946,14.315248435333686 40.556706911840905,13.818379707800878 41.54558876800179,13.800550898366621 41.54970986158542,14.676121401599385</gml:coordinates>\n </gml:LinearRing>\n </gml:outerBoundaryIs>\n</gml:Polygon>'], dtype=object)
- footprint(index)object'MULTIPOLYGON (((13.818379707800…
array(['MULTIPOLYGON (((13.818379707800878 40.556706911840905, 14.315248435333686 40.55900004804946, 14.328944144185272 40.597110577059766, 14.381979877189965 40.74322865985612, 14.434900408922116 40.88941849252913, 14.439669454929737 40.90252914944609, 14.48834005349154 41.036462304690495, 14.500741494562956 41.07059614201777, 14.54105211414984 41.18196503364706, 14.593549486819448 41.328546882077774, 14.617403784322985 41.39209831941913, 14.648566945908971 41.475316413364425, 14.676121401599385 41.54970986158542, 13.800550898366621 41.54558876800179, 13.818379707800878 40.556706911840905)))'], dtype=object)
- format(index)object'SAFE'
array(['SAFE'], dtype=object)
- processingbaseline(index)object'04.00'
array(['04.00'], dtype=object)
- platformname(index)object'Sentinel-2'
array(['Sentinel-2'], dtype=object)
- filename(index)object'S2B_MSIL2A_20220430T100019_N040…
array(['S2B_MSIL2A_20220430T100019_N0400_R122_T33TVF_20220430T131131.SAFE'], dtype=object)
- instrumentname(index)object'Multi-Spectral Instrument'
array(['Multi-Spectral Instrument'], dtype=object)
- instrumentshortname(index)object'MSI'
array(['MSI'], dtype=object)
- size(index)object'624.78 MB'
array(['624.78 MB'], dtype=object)
- s2datatakeid(index)object'GS2B_20220430T100019_026889_N04…
array(['GS2B_20220430T100019_026889_N04.00'], dtype=object)
- producttype(index)object'S2MSI2A'
array(['S2MSI2A'], dtype=object)
- platformidentifier(index)object'2017-013A'
array(['2017-013A'], dtype=object)
- orbitdirection(index)object'DESCENDING'
array(['DESCENDING'], dtype=object)
- platformserialidentifier(index)object'Sentinel-2B'
array(['Sentinel-2B'], dtype=object)
- processinglevel(index)object'Level-2A'
array(['Level-2A'], dtype=object)
- datastripidentifier(index)object'S2B_OPER_MSI_L2A_DS_2BPS_202204…
array(['S2B_OPER_MSI_L2A_DS_2BPS_20220430T131131_S20220430T100356_N04.00'], dtype=object)
- granuleidentifier(index)object'S2B_OPER_MSI_L2A_TL_2BPS_202204…
array(['S2B_OPER_MSI_L2A_TL_2BPS_20220430T131131_A026889_T33TVF_N04.00'], dtype=object)
- identifier(index)object'S2B_MSIL2A_20220430T100019_N040…
array(['S2B_MSIL2A_20220430T100019_N0400_R122_T33TVF_20220430T131131'], dtype=object)
- uuid(index)object'b5c7ca6d-768e-4600-a2dd-556d31b…
array(['b5c7ca6d-768e-4600-a2dd-556d31b05693'], dtype=object)
Testing Download API by using the id product¶
Before download sentinel data, control if the product is Online and able to directly download. Otherwise you must unblock the product by swutching it from OFFLINE status to ONLINE status.
product_info = api.get_product_odata('8a464f41-cb80-4350-848f-a95da9810991')
product_info['Online']
False
api.download('8a464f41-cb80-4350-848f-a95da9810991')
Downloading S1B_IW_SLC__1SDV_20180607T041420_20180607T041448_011262_014ABB_AC0E.zip: 0%| | 0.00/4.5…
MD5 checksumming: 0%| | 0.00/4.50G [00:00<?, ?B/s]
{'id': '8a464f41-cb80-4350-848f-a95da9810991', 'title': 'S1B_IW_SLC__1SDV_20180607T041420_20180607T041448_011262_014ABB_AC0E', 'size': 4496221397, 'md5': 'cd13a4fe35c5c197ca83c75c7acf036f', 'date': datetime.datetime(2018, 6, 7, 4, 14, 20, 208000), 'footprint': 'POLYGON((28.414934 37.326553,25.574972 37.724804,25.919802 39.401939,28.828909 39.004982,28.414934 37.326553))', 'url': "https://apihub.copernicus.eu/apihub/odata/v1/Products('8a464f41-cb80-4350-848f-a95da9810991')/$value", 'Online': True, 'Creation Date': datetime.datetime(2018, 6, 7, 9, 21, 30, 469000), 'Ingestion Date': datetime.datetime(2018, 6, 7, 9, 13, 12, 2000), 'quicklook_url': "https://apihub.copernicus.eu/apihub/odata/v1/Products('8a464f41-cb80-4350-848f-a95da9810991')/Products('Quicklook')/$value", 'path': 'S1B_IW_SLC__1SDV_20180607T041420_20180607T041448_011262_014ABB_AC0E.zip', 'downloaded_bytes': 4496221397}
In this case I select a product offline. It exists an API (trigger_offline_retrieval) in which we pass the id product in order to re-activate the product in 24h. The response of server is a boolean (True) for the success of request. Then you could try to download your desired data.
api.trigger_offline_retrieval('8a464f41-cb80-4350-848f-a95da9810991')
True
Displaying product info¶
products_df.info()
<class 'pandas.core.frame.DataFrame'> Index: 4 entries, 87438c76-c585-43d6-875d-490056fab559 to be152722-3387-4d7c-a9fa-0ce74f824e9c Data columns (total 34 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 title 4 non-null object 1 link 4 non-null object 2 link_alternative 4 non-null object 3 link_icon 4 non-null object 4 summary 4 non-null object 5 ondemand 4 non-null object 6 ingestiondate 4 non-null datetime64[ns] 7 beginposition 4 non-null datetime64[ns] 8 endposition 4 non-null datetime64[ns] 9 missiondatatakeid 4 non-null int64 10 orbitnumber 4 non-null int64 11 lastorbitnumber 4 non-null int64 12 relativeorbitnumber 4 non-null int64 13 lastrelativeorbitnumber 4 non-null int64 14 slicenumber 4 non-null int64 15 acquisitiontype 4 non-null object 16 filename 4 non-null object 17 gmlfootprint 4 non-null object 18 format 4 non-null object 19 identifier 4 non-null object 20 instrumentshortname 4 non-null object 21 sensoroperationalmode 4 non-null object 22 instrumentname 4 non-null object 23 swathidentifier 4 non-null object 24 footprint 4 non-null object 25 platformidentifier 4 non-null object 26 orbitdirection 4 non-null object 27 polarisationmode 4 non-null object 28 productclass 4 non-null object 29 producttype 4 non-null object 30 platformname 4 non-null object 31 size 4 non-null object 32 status 4 non-null object 33 uuid 4 non-null object dtypes: datetime64[ns](3), int64(6), object(25) memory usage: 1.1+ KB
If we want to display all the information within the columns of Panda Dataframe, we must set the max number of columns we need to visualize. I set an hight number in order to all visualize.
pd.set_option('display.max_columns', 100)
products_sorted
Sentinelsat allows filtering and sorting of search results before download.
products_sorted = products_df.sort_values(['size'], ascending=[True])
products_sorted