Download Sentinel Data using Python from Copernicus

Krishna G. Lodha
Rotten Grapes
Published in
3 min readJan 21, 2024

Downloading Raw Sentinel data via python is easier than ever, in this blog we’ll checkout steps to setup your account and start downloading sentinel data for free.

Create account

Create free account at Copernicus , by filling up name, email, password and basic use case information. Once the form is filled up, you’ll receive email for account verification, please verify account and keep your email and password handy.

Create Python file

Now we’ll start creating python file to download the data. If you want to get the file directly, you can checkout the gist

Here is the explanation of the code written in above gist,

Import packages

Once installation is done based on requirements.txt file mentioned in gist, start by importing necessary packages


from datetime import date, timedelta # To define date range of data
import requests # To define http request to be make
import pandas as pd # Convert data received from copernicus API in easier format
import geopandas as gpd # Convert Pandas dataframe in Geo pandas will allow us to use metadata and geoemtry.
from shapely.geometry import shape # To convert raw Geometry data

Setup required variables

Setup variables with values according to your use case

# copernicus User email
copernicus_user = os.getenv("copernicus_user")
# copernicus User Password
copernicus_password = os.getenv("copernicus_password")
# WKT Representation of BBOX of AOI
ft = "POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))"
# Sentinel satellite that you are interested in
data_collection = "SENTINEL-2"

# Dates of search

today = date.today()
today_string = today.strftime("%Y-%m-%d")
yesterday = today - timedelta(days=1)
yesterday_string = yesterday.strftime("%Y-%m-%d")

Generate access token

Setup function to get access token from copernicus based on username and password provided in variables above

def get_keycloak(username: str, password: str) -> str:
data = {
"client_id": "cdse-public",
"username": username,
"password": password,
"grant_type": "password",
}
try:
r = requests.post(
"https://identity.dataspace.copernicus.eu/auth/realms/CDSE/protocol/openid-connect/token",
data=data,
)
r.raise_for_status()
except Exception as e:
raise Exception(
f"Keycloak token creation failed. Reponse from the server was: {r.json()}"
)
return r.json()["access_token"]

finally, we’ll create code to use all variables and access token to hit API and get data

json_ = requests.get(
f"https://catalogue.dataspace.copernicus.eu/odata/v1/Products?$filter=Collection/Name eq '{data_collection}' and OData.CSC.Intersects(area=geography'SRID=4326;{ft}') and ContentDate/Start gt {yesterday_string}T00:00:00.000Z and ContentDate/Start lt {today_string}T00:00:00.000Z&$count=True&$top=1000"
).json()
p = pd.DataFrame.from_dict(json_["value"]) # Fetch available dataset
if p.shape[0] > 0 : # If we get data back
p["geometry"] = p["GeoFootprint"].apply(shape)
# Convert pandas dataframe to Geopandas dataframe by setting up geometry
productDF = gpd.GeoDataFrame(p).set_geometry("geometry")
# Remove L1C dataset if not needed
productDF = productDF[~productDF["Name"].str.contains("L1C")]
print(f" total L2A tiles found {len(productDF)}")
productDF["identifier"] = productDF["Name"].str.split(".").str[0]
allfeat = len(productDF)

if allfeat == 0: # If L2A tiles are not available in current query
print(f"No tiles found for {today_string}")
else: # If L2A tiles are available in current query
# download all tiles from server
for index,feat in enumerate(productDF.iterfeatures()):
try:
# Create requests session
session = requests.Session()
# Get access token based on username and password
keycloak_token = get_keycloak(copernicus_user,copernicus_password)

session.headers.update({"Authorization": f"Bearer {keycloak_token}"})
url = f"https://catalogue.dataspace.copernicus.eu/odata/v1/Products({feat['properties']['Id']})/$value"
response = session.get(url, allow_redirects=False)
while response.status_code in (301, 302, 303, 307):
url = response.headers["Location"]
response = session.get(url, allow_redirects=False)
print(feat["properties"]["Id"])
file = session.get(url, verify=False, allow_redirects=True)

with open(
f"location/to/save/{feat['properties']['identifier']}.zip", #location to save zip from copernicus
"wb",
) as p:
print(feat["properties"]["Name"])
p.write(file.content)
except:
print("problem with server")
else : # If no tiles found for given date range and AOI
print('no data found')

Further enhancements

You can write further code to unzip the dowloaded files from server and use raw images to created desired outputs such as NDVI, EVI, etc.

If you are interested in blog/video for the same, reach out to me over email or in comments.

--

--