Sentinel-2 snow cover#
Import libraries#
# Sentinel Hub
from sentinelhub import (SHConfig, SentinelHubRequest, DataCollection, MimeType, CRS, BBox, bbox_to_dimensions, geometry)
# Geospatial libraries
import geopandas as gpd
from datetime import date
import folium
Sentinel-hub authentication#
config = SHConfig()
config.sh_client_id = %env SH_CLIENT_ID
config.sh_client_secret = %env SH_CLIENT_SECRET
Input definition#
Read the shapefile on which to compute the snow cover map and plot it
# Read the file with geopandas and convert it to EPSG:4326
aoi_df = gpd.read_file('ADO_DSC_ITH1_0025.geojson')
aoi_df = aoi_df.to_crs(4326)
# Create a basemap with folium
m = folium.Map(
location=[aoi_df.to_crs(4326).iloc[0].geometry.centroid.y, aoi_df.to_crs(4326).iloc[0].geometry.centroid.x],
zoom_start=11)
# Add the polygon to the map
geo_j = folium.GeoJson(data=aoi_df.to_json())
geo_j.add_to(m)
# Show the map
m
Make this Notebook Trusted to load map: File -> Trust Notebook
Select a date
start_date = date(2018, 4, 1)
end_date = date(2018, 4, 15)
Snowcover map generation#
Generate the snow cover map on the selected AOI and date from S2L1C
evalscript_snowcover = """
//VERSION=3
function setup() {
return {
input: ["B03", "B04", "B08", "B11", "CLM", "dataMask"],
output: {bands: 1, sampleType: "UINT8"}
};
}
function calc_snow_mask(NDVI, NDSI, B03){
let si = (NDSI >= 0.4) ? 1 : (Math.abs(NDVI - 0.1) <= 0.025 ? 1 : 0);
let br = B03 > 0.3;
return si && br;
}
function evaluatePixel(sample) {
// Calculate indices
var NDSI = (sample.B03 - sample.B11) / (sample.B03 + sample.B11);
var NDVI = (sample.B08 - sample.B04) / (sample.B08 + sample.B04);
// Calculate snowmask
var snow = calc_snow_mask(NDVI, NDSI, sample.B03);
// Create the snow mask with clouds
if (sample.dataMask == 0){
return [0];
}
else if (sample.CLM == 1){
return [3];
}
else if (snow == 1){
return [1];
}
else if (snow == 0){
return [2];
}
}
"""
aoi = geometry.Geometry(aoi_df.geometry.iloc[0], CRS.WGS84)
request_snowcover = SentinelHubRequest(
evalscript=evalscript_snowcover,
input_data=[
SentinelHubRequest.input_data(
data_collection=DataCollection.SENTINEL2_L1C,
time_interval=(start_date, end_date),
)
],
responses=[SentinelHubRequest.output_response('default', MimeType.TIFF)],
geometry=aoi,
size=bbox_to_dimensions(aoi.bbox, resolution=100),
config=config
)
snowcover = request_snowcover.get_data()
len(snowcover)
1
Generate the corresponding RGB image
# The evalscript for RGB map creation
evalscript_rgb = """
//VERSION=3
function setup(){
return{
input: ["B02", "B03", "B04", "dataMask"],
output: {bands: 4}
}
}
function evaluatePixel(sample){
// Set gain for visualisation
let gain = 2.5;
// Return RGB
return [sample.B04 * gain, sample.B03 * gain, sample.B02 * gain, sample.dataMask];
}
"""
request_rgb = SentinelHubRequest(
evalscript=evalscript_rgb,
input_data=[
SentinelHubRequest.input_data(
data_collection=DataCollection.SENTINEL2_L1C,
time_interval=(date, date),
)],
responses=[
SentinelHubRequest.output_response('default', MimeType.TIFF)
],
geometry=aoi,
size=bbox_to_dimensions(aoi.bbox, resolution=10),
config=config
)
rgb_response = request_rgb.get_data()
---------------------------------------------------------------------------
HTTPError Traceback (most recent call last)
File /home/conda/master-asi-conae/315434deb1b1e05eacacc41e1b0529b1837100f46402a32041d77ba54ae6c5f3-20230213-125825-013239-74-edc-2022.10-14/lib/python3.9/site-packages/sentinelhub/download/handlers.py:41, in fail_user_errors.<locals>.new_download_func(self, request)
40 try:
---> 41 return download_func(self, request)
42 except requests.HTTPError as exception:
File /home/conda/master-asi-conae/315434deb1b1e05eacacc41e1b0529b1837100f46402a32041d77ba54ae6c5f3-20230213-125825-013239-74-edc-2022.10-14/lib/python3.9/site-packages/sentinelhub/download/sentinelhub_client.py:90, in SentinelHubDownloadClient._execute_download(self, request)
88 continue
---> 90 response.raise_for_status()
92 LOGGER.debug("Successful %s request to %s", request.request_type.value, request.url)
File /home/conda/master-asi-conae/315434deb1b1e05eacacc41e1b0529b1837100f46402a32041d77ba54ae6c5f3-20230213-125825-013239-74-edc-2022.10-14/lib/python3.9/site-packages/requests/models.py:1021, in Response.raise_for_status(self)
1020 if http_error_msg:
-> 1021 raise HTTPError(http_error_msg, response=self)
HTTPError: 400 Client Error: Bad Request for url: https://services.sentinel-hub.com/api/v1/process
The above exception was the direct cause of the following exception:
DownloadFailedException Traceback (most recent call last)
Cell In[10], line 32
2 evalscript_rgb = """
3 //VERSION=3
4 function setup(){
(...)
16 }
17 """
18 request_rgb = SentinelHubRequest(
19 evalscript=evalscript_rgb,
20 input_data=[
(...)
30 config=config
31 )
---> 32 rgb_response = request_rgb.get_data()
File /home/conda/master-asi-conae/315434deb1b1e05eacacc41e1b0529b1837100f46402a32041d77ba54ae6c5f3-20230213-125825-013239-74-edc-2022.10-14/lib/python3.9/site-packages/sentinelhub/base.py:110, in DataRequest.get_data(self, save_data, redownload, data_filter, max_threads, decode_data, raise_download_errors, show_progress)
88 """Get requested data either by downloading it or by reading it from the disk (if it
89 was previously downloaded and saved).
90
(...)
107 shape ``[height, width, channels]``.
108 """
109 self._preprocess_request(save_data, True)
--> 110 return self._execute_data_download(
111 data_filter,
112 redownload,
113 max_threads,
114 raise_download_errors,
115 decode_data=decode_data,
116 show_progress=show_progress,
117 )
File /home/conda/master-asi-conae/315434deb1b1e05eacacc41e1b0529b1837100f46402a32041d77ba54ae6c5f3-20230213-125825-013239-74-edc-2022.10-14/lib/python3.9/site-packages/sentinelhub/base.py:184, in DataRequest._execute_data_download(self, data_filter, redownload, max_threads, raise_download_errors, decode_data, show_progress)
179 raise ValueError("data_filter parameter must be a list of indices")
181 client = self.download_client_class(
182 redownload=redownload, raise_download_errors=raise_download_errors, config=self.config
183 )
--> 184 data_list = client.download(
185 filtered_download_list, max_threads=max_threads, decode_data=decode_data, show_progress=show_progress
186 )
188 if is_repeating_filter:
189 data_list = [copy.deepcopy(data_list[index]) for index in mapping_list]
File /home/conda/master-asi-conae/315434deb1b1e05eacacc41e1b0529b1837100f46402a32041d77ba54ae6c5f3-20230213-125825-013239-74-edc-2022.10-14/lib/python3.9/site-packages/sentinelhub/download/sentinelhub_client.py:62, in SentinelHubDownloadClient.download(self, *args, **kwargs)
60 self.lock = Lock()
61 try:
---> 62 return super().download(*args, **kwargs)
63 finally:
64 self.lock = None
File /home/conda/master-asi-conae/315434deb1b1e05eacacc41e1b0529b1837100f46402a32041d77ba54ae6c5f3-20230213-125825-013239-74-edc-2022.10-14/lib/python3.9/site-packages/sentinelhub/download/client.py:113, in DownloadClient.download(self, download_requests, max_threads, decode_data, show_progress)
111 else:
112 for future in as_completed(download_list):
--> 113 data_list[future_order[future]] = self._process_download_future(future)
115 if isinstance(download_requests, DownloadRequest):
116 return data_list[0]
File /home/conda/master-asi-conae/315434deb1b1e05eacacc41e1b0529b1837100f46402a32041d77ba54ae6c5f3-20230213-125825-013239-74-edc-2022.10-14/lib/python3.9/site-packages/sentinelhub/download/client.py:126, in DownloadClient._process_download_future(self, future)
124 if self.raise_download_errors:
125 traceback = sys.exc_info()[2]
--> 126 raise download_exception.with_traceback(traceback)
128 warnings.warn(str(download_exception), category=SHRuntimeWarning)
129 return None
File /home/conda/master-asi-conae/315434deb1b1e05eacacc41e1b0529b1837100f46402a32041d77ba54ae6c5f3-20230213-125825-013239-74-edc-2022.10-14/lib/python3.9/site-packages/sentinelhub/download/client.py:122, in DownloadClient._process_download_future(self, future)
120 """Unpacks the future and correctly handles exceptions"""
121 try:
--> 122 return future.result()
123 except DownloadFailedException as download_exception:
124 if self.raise_download_errors:
File /home/conda/master-asi-conae/315434deb1b1e05eacacc41e1b0529b1837100f46402a32041d77ba54ae6c5f3-20230213-125825-013239-74-edc-2022.10-14/lib/python3.9/concurrent/futures/_base.py:439, in Future.result(self, timeout)
437 raise CancelledError()
438 elif self._state == FINISHED:
--> 439 return self.__get_result()
441 self._condition.wait(timeout)
443 if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
File /home/conda/master-asi-conae/315434deb1b1e05eacacc41e1b0529b1837100f46402a32041d77ba54ae6c5f3-20230213-125825-013239-74-edc-2022.10-14/lib/python3.9/concurrent/futures/_base.py:391, in Future.__get_result(self)
389 if self._exception:
390 try:
--> 391 raise self._exception
392 finally:
393 # Break a reference cycle with the exception in self._exception
394 self = None
File /home/conda/master-asi-conae/315434deb1b1e05eacacc41e1b0529b1837100f46402a32041d77ba54ae6c5f3-20230213-125825-013239-74-edc-2022.10-14/lib/python3.9/concurrent/futures/thread.py:58, in _WorkItem.run(self)
55 return
57 try:
---> 58 result = self.fn(*self.args, **self.kwargs)
59 except BaseException as exc:
60 self.future.set_exception(exc)
File /home/conda/master-asi-conae/315434deb1b1e05eacacc41e1b0529b1837100f46402a32041d77ba54ae6c5f3-20230213-125825-013239-74-edc-2022.10-14/lib/python3.9/site-packages/sentinelhub/download/client.py:133, in DownloadClient._single_download_decoded(self, request)
131 def _single_download_decoded(self, request: DownloadRequest) -> Any:
132 """Downloads a response and decodes it into data. By decoding a single response"""
--> 133 response = self._single_download(request)
134 return None if response is None else response.decode()
File /home/conda/master-asi-conae/315434deb1b1e05eacacc41e1b0529b1837100f46402a32041d77ba54ae6c5f3-20230213-125825-013239-74-edc-2022.10-14/lib/python3.9/site-packages/sentinelhub/download/client.py:146, in DownloadClient._single_download(self, request)
144 no_local_data = self.redownload or response_path is None or not os.path.exists(response_path)
145 if no_local_data:
--> 146 response = self._execute_download(request)
147 else:
148 if not request.return_data or response_path is None:
File /home/conda/master-asi-conae/315434deb1b1e05eacacc41e1b0529b1837100f46402a32041d77ba54ae6c5f3-20230213-125825-013239-74-edc-2022.10-14/lib/python3.9/site-packages/sentinelhub/download/handlers.py:67, in retry_temporary_errors.<locals>.new_download_func(self, request)
65 for attempt_num in range(download_attempts):
66 try:
---> 67 return download_func(self, request)
68 except requests.RequestException as exception:
69 if not (
70 _is_temporary_problem(exception)
71 or (
(...)
74 )
75 ):
File /home/conda/master-asi-conae/315434deb1b1e05eacacc41e1b0529b1837100f46402a32041d77ba54ae6c5f3-20230213-125825-013239-74-edc-2022.10-14/lib/python3.9/site-packages/sentinelhub/download/handlers.py:47, in fail_user_errors.<locals>.new_download_func(self, request)
42 except requests.HTTPError as exception:
43 if (
44 exception.response.status_code < requests.status_codes.codes.INTERNAL_SERVER_ERROR
45 and exception.response.status_code != requests.status_codes.codes.TOO_MANY_REQUESTS
46 ):
---> 47 raise DownloadFailedException(
48 _create_download_failed_message(exception, request.url), request_exception=exception
49 ) from exception
50 raise exception from exception
DownloadFailedException: Failed to download from:
https://services.sentinel-hub.com/api/v1/process
with HTTPError:
400 Client Error: Bad Request for url: https://services.sentinel-hub.com/api/v1/process
Server response: "{"status": 400, "reason": "Bad Request", "message": "Invalid request", "code": "COMMON_BAD_PAYLOAD", "errors": [{"parameter": "output->width", "invalidValue": 2881, "violation": "must be less than or equal to 2500", "description": "The request image width. Must be an integer between 1 and 2500. <br />*Only one pair of parameters \"width\"/\"height\" or \"resx\"/\"resy\" must be set at the same time.*"}, {"parameter": "output->height", "invalidValue": 3058, "violation": "must be less than or equal to 2500", "description": "The request image height. Must be an integer between 1 and 2500. <br />*Only one pair of parameters \"width\"/\"height\" or \"resx\"/\"resy\" must be set at the same time.*"}]}"
Show the result#
# Create a basemap with folium
m = folium.Map(
location=aoi.bbox.middle[::-1],
zoom_start=11)
# Color map for the snow cover (0 no data: transparent, 1 snow: white, 2 snow-free: green, 3 cloud: gray)
cmap = ((0, 0, 0, 0), (1, 1, 1, 1), (0, 0.6, 0.3, 1), (0.5, 0.5, 0.5, 1))
# Add the snow cover to the map
folium.raster_layers.ImageOverlay(
snowcover[0],
bounds=[aoi.bbox.lower_left[::-1], aoi.bbox.upper_right[::-1]],
name='snow cover',
colormap=lambda x: cmap[x]
).add_to(m)
# Add the Sentinel-2 RGB to the map
folium.raster_layers.ImageOverlay(
rgb_response[0]/255,
bounds=[aoi.bbox.lower_left[::-1], aoi.bbox.upper_right[::-1]],
name='RGB',
).add_to(m)
# Add the layer control
folium.LayerControl().add_to(m)
# Show the map
m
snowcover
[array([[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]], dtype=uint8)]
# Create a basemap with folium
m = folium.Map(
location=aoi.bbox.middle[::-1],
zoom_start=11)
# Color map for the snow cover (0 no data: transparent, 1 snow: white, 2 snow-free: green, 3 cloud: gray)
cmap = ((0, 0, 0, 0), (1, 1, 1, 1), (0, 0.6, 0.3, 1), (0.5, 0.5, 0.5, 1))
# Add the snow cover to the map
folium.raster_layers.ImageOverlay(
snowcover[0],
bounds=[aoi.bbox.lower_left[::-1], aoi.bbox.upper_right[::-1]],
name='snow cover',
colormap=lambda x: cmap[x]
).add_to(m)
<folium.raster_layers.ImageOverlay at 0x7ff35780afa0>
m
Make this Notebook Trusted to load map: File -> Trust Notebook