Skip to content

Instantly share code, notes, and snippets.

@giswqs
Created April 5, 2021 20:07
Show Gist options
  • Save giswqs/f02b89fdc68b59ebf79f436662595048 to your computer and use it in GitHub Desktop.
Save giswqs/f02b89fdc68b59ebf79f436662595048 to your computer and use it in GitHub Desktop.
s2.ipynb
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"metadata": {
"id": "8kdsGkYJXXKc",
"trusted": true
},
"cell_type": "code",
"source": "#@title Copyright 2020 The Earth Engine Community Authors { display-mode: \"form\" }\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.",
"execution_count": 3,
"outputs": []
},
{
"metadata": {
"id": "l18M9_r5XmAQ"
},
"cell_type": "markdown",
"source": "# Sentinel-2 Cloud Masking with s2cloudless\n\nAuthor: jdbcode\n"
},
{
"metadata": {
"id": "h2i3NKIk4nWq"
},
"cell_type": "markdown",
"source": "This tutorial is an introduction to masking clouds and cloud shadows in Sentinel-2 (S2) surface reflectance (SR) data using Earth Engine. Clouds are identified from the S2 cloud probability dataset (s2cloudless) and shadows are defined by cloud projection intersection with low-reflectance near-infrared (NIR) pixels."
},
{
"metadata": {
"id": "U7i55vr_aKCB"
},
"cell_type": "markdown",
"source": "### Run me first\n\nRun the following cell to initialize the Earth Engine API. The output will contain instructions on how to grant this notebook access to Earth Engine using your account."
},
{
"metadata": {
"id": "XeFsiSp2aDL6",
"trusted": true
},
"cell_type": "code",
"source": "import ee\nimport geemap\n# Trigger the authentication flow.\n# ee.Authenticate()\n\n# # Initialize the library.\n# ee.Initialize()",
"execution_count": 4,
"outputs": []
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "Map = geemap.Map()",
"execution_count": 5,
"outputs": []
},
{
"metadata": {
"id": "U0xJ-vCrWF1S"
},
"cell_type": "markdown",
"source": "## Assemble cloud mask components\n\nThis section builds an S2 SR collection and defines functions to add cloud and cloud shadow component layers to each image in the collection.\n\n"
},
{
"metadata": {
"id": "tCRB28UkJJjp"
},
"cell_type": "markdown",
"source": "### Define collection filter and cloud mask parameters\n\nDefine parameters that are used to filter the S2 image collection and determine cloud and cloud shadow identification.\n\n|Parameter | Type | Description |\n| :-- | :-- | :-- |\n| `AOI` | `ee.Geometry` | Area of interest |\n| `START_DATE` | string | Image collection start date (inclusive) |\n| `END_DATE` | string | Image collection end date (exclusive) |\n| `CLOUD_FILTER` | integer | Maximum image cloud cover percent allowed in image collection |\n| `CLD_PRB_THRESH` | integer | Cloud probability (%); values greater than are considered cloud |\n| `NIR_DRK_THRESH` | float | Near-infrared reflectance; values less than are considered potential cloud shadow |\n| `CLD_PRJ_DIST` | float | Maximum distance (km) to search for cloud shadows from cloud edges |\n| `BUFFER` | integer | Distance (m) to dilate the edge of cloud-identified objects |"
},
{
"metadata": {
"id": "q9LnKA3kEVrw"
},
"cell_type": "markdown",
"source": "The values currently set for `AOI`, `START_DATE`, `END_DATE`, and `CLOUD_FILTER` are intended to build a collection for a single S2 overpass of a region near Portland, Oregon, USA. When parameterizing and evaluating cloud masks for a new area, it is good practice to identify a single overpass date and limit the regional extent to minimize processing requirements. If you want to work with a different example, use this [Earth Engine App](https://showcase.earthengine.app/view/s2-sr-browser-s2cloudless-nb) to identify an image that includes some clouds, then replace the relevant parameter values below with those provided in the app."
},
{
"metadata": {
"id": "qBjBmX4-WFG7",
"trusted": true
},
"cell_type": "code",
"source": "AOI = ee.Geometry.Point(-122.269, 45.701)\nSTART_DATE = '2020-06-01'\nEND_DATE = '2020-06-02'\nCLOUD_FILTER = 60\nCLD_PRB_THRESH = 50\nNIR_DRK_THRESH = 0.15\nCLD_PRJ_DIST = 1\nBUFFER = 50",
"execution_count": 6,
"outputs": []
},
{
"metadata": {
"id": "DnJGSW1MlEL2"
},
"cell_type": "markdown",
"source": "### Build a Sentinel-2 collection\n\n[Sentinel-2 surface reflectance](https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_SR) and [Sentinel-2 cloud probability](https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_CLOUD_PROBABILITY) are two different image collections. Each collection must be filtered similarly (e.g., by date and bounds) and then the two filtered collections must be joined.\n\nDefine a function to filter the SR and s2cloudless collections according to area of interest and date parameters, then join them on the `system:index` property. The result is a copy of the SR collection where each image has a new `'s2cloudless'` property whose value is the corresponding s2cloudless image."
},
{
"metadata": {
"id": "eCp0jiy2WBSz",
"trusted": true
},
"cell_type": "code",
"source": "def get_s2_sr_cld_col(aoi, start_date, end_date):\n # Import and filter S2 SR.\n s2_sr_col = (ee.ImageCollection('COPERNICUS/S2_SR')\n .filterBounds(aoi)\n .filterDate(start_date, end_date)\n .filter(ee.Filter.lte('CLOUDY_PIXEL_PERCENTAGE', CLOUD_FILTER)))\n\n # Import and filter s2cloudless.\n s2_cloudless_col = (ee.ImageCollection('COPERNICUS/S2_CLOUD_PROBABILITY')\n .filterBounds(aoi)\n .filterDate(start_date, end_date))\n\n # Join the filtered s2cloudless collection to the SR collection by the 'system:index' property.\n return ee.ImageCollection(ee.Join.saveFirst('s2cloudless').apply(**{\n 'primary': s2_sr_col,\n 'secondary': s2_cloudless_col,\n 'condition': ee.Filter.equals(**{\n 'leftField': 'system:index',\n 'rightField': 'system:index'\n })\n }))",
"execution_count": 7,
"outputs": []
},
{
"metadata": {
"id": "G2SNDLIKkaZS"
},
"cell_type": "markdown",
"source": "Apply the `get_s2_sr_cld_col` function to build a collection according to the parameters defined above."
},
{
"metadata": {
"id": "WhpT9SpeW0W2",
"trusted": true
},
"cell_type": "code",
"source": "s2_sr_cld_col_eval = get_s2_sr_cld_col(AOI, START_DATE, END_DATE)",
"execution_count": 8,
"outputs": []
},
{
"metadata": {
"id": "56S6gZNUr2n8"
},
"cell_type": "markdown",
"source": "### Define cloud mask component functions"
},
{
"metadata": {
"id": "ZUyuXh7gP-98"
},
"cell_type": "markdown",
"source": "#### Cloud components\n\nDefine a function to add the s2cloudless probability layer and derived cloud mask as bands to an S2 SR image input."
},
{
"metadata": {
"id": "yE9x4KREYfnD",
"trusted": true
},
"cell_type": "code",
"source": "def add_cloud_bands(img):\n # Get s2cloudless image, subset the probability band.\n cld_prb = ee.Image(img.get('s2cloudless')).select('probability')\n\n # Condition s2cloudless by the probability threshold value.\n is_cloud = cld_prb.gt(CLD_PRB_THRESH).rename('clouds')\n\n # Add the cloud probability layer and cloud mask as image bands.\n return img.addBands(ee.Image([cld_prb, is_cloud]))",
"execution_count": 9,
"outputs": []
},
{
"metadata": {
"id": "HUnkfgniKzVr"
},
"cell_type": "markdown",
"source": "#### Cloud shadow components\n\nDefine a function to add dark pixels, cloud projection, and identified shadows as bands to an S2 SR image input. Note that the image input needs to be the result of the above `add_cloud_bands` function because it relies on knowing which pixels are considered cloudy (`'clouds'` band)."
},
{
"metadata": {
"id": "zU-uPdhtbzw3",
"trusted": true
},
"cell_type": "code",
"source": "def add_shadow_bands(img):\n # Identify water pixels from the SCL band.\n not_water = img.select('SCL').neq(6)\n\n # Identify dark NIR pixels that are not water (potential cloud shadow pixels).\n SR_BAND_SCALE = 1e4\n dark_pixels = img.select('B8').lt(NIR_DRK_THRESH*SR_BAND_SCALE).multiply(not_water).rename('dark_pixels')\n\n # Determine the direction to project cloud shadow from clouds (assumes UTM projection).\n shadow_azimuth = ee.Number(90).subtract(ee.Number(img.get('MEAN_SOLAR_AZIMUTH_ANGLE')));\n\n # Project shadows from clouds for the distance specified by the CLD_PRJ_DIST input.\n cld_proj = (img.select('clouds').directionalDistanceTransform(shadow_azimuth, CLD_PRJ_DIST*10)\n .reproject(**{'crs': img.select(0).projection(), 'scale': 100})\n .select('distance')\n .mask()\n .rename('cloud_transform'))\n\n # Identify the intersection of dark pixels with cloud shadow projection.\n shadows = cld_proj.multiply(dark_pixels).rename('shadows')\n\n # Add dark pixels, cloud projection, and identified shadows as image bands.\n return img.addBands(ee.Image([dark_pixels, cld_proj, shadows]))",
"execution_count": 10,
"outputs": []
},
{
"metadata": {
"id": "2rqSRgn1NfxZ"
},
"cell_type": "markdown",
"source": "#### Final cloud-shadow mask\n\nDefine a function to assemble all of the cloud and cloud shadow components and produce the final mask."
},
{
"metadata": {
"id": "VNP-VlmwccKG",
"trusted": true
},
"cell_type": "code",
"source": "def add_cld_shdw_mask(img):\n # Add cloud component bands.\n img_cloud = add_cloud_bands(img)\n\n # Add cloud shadow component bands.\n img_cloud_shadow = add_shadow_bands(img_cloud)\n\n # Combine cloud and shadow mask, set cloud and shadow as value 1, else 0.\n is_cld_shdw = img_cloud_shadow.select('clouds').add(img_cloud_shadow.select('shadows')).gt(0)\n\n # Remove small cloud-shadow patches and dilate remaining pixels by BUFFER input.\n # 20 m scale is for speed, and assumes clouds don't require 10 m precision.\n is_cld_shdw = (is_cld_shdw.focal_min(2).focal_max(BUFFER*2/20)\n .reproject(**{'crs': img.select([0]).projection(), 'scale': 20})\n .rename('cloudmask'))\n\n # Add the final cloud-shadow mask to the image.\n return img_cloud_shadow.addBands(is_cld_shdw)",
"execution_count": 11,
"outputs": []
},
{
"metadata": {
"id": "i0QbQoJ1jpHw"
},
"cell_type": "markdown",
"source": "## Visualize and evaluate cloud mask components\n\nThis section provides functions for displaying the cloud and cloud shadow components. In most cases, adding all components to images and viewing them is unnecessary. This section is included to illustrate how the cloud/cloud shadow mask is developed and demonstrate how to test and evaluate various parameters, which is helpful when defining masking variables for an unfamiliar region or time of year.\n\nIn applications outside of this tutorial, if you prefer to include only the final cloud/cloud shadow mask along with the original image bands, replace:\n\n```\nreturn img_cloud_shadow.addBands(is_cld_shdw)\n```\n\nwith\n\n```\nreturn img.addBands(is_cld_shdw)\n```\n\nin the above `add_cld_shdw_mask` function."
},
{
"metadata": {
"id": "E_kxYHOAjvu2"
},
"cell_type": "markdown",
"source": "### Define functions to display image and mask component layers.\n\nFolium will be used to display map layers. Import `folium` and define a method to display Earth Engine image tiles."
},
{
"metadata": {
"id": "JPjOhSvGZAY8",
"trusted": true
},
"cell_type": "code",
"source": "# Import the folium library.\nimport folium\n\n# Define a method for displaying Earth Engine image tiles to a folium map.\ndef add_ee_layer(self, ee_image_object, vis_params, name, show=True, opacity=1, min_zoom=0):\n map_id_dict = ee.Image(ee_image_object).getMapId(vis_params)\n folium.raster_layers.TileLayer(\n tiles=map_id_dict['tile_fetcher'].url_format,\n attr='Map Data &copy; <a href=\"https://earthengine.google.com/\">Google Earth Engine</a>',\n name=name,\n show=show,\n opacity=opacity,\n min_zoom=min_zoom,\n overlay=True,\n control=True\n ).add_to(self)\n\n# Add the Earth Engine layer method to folium.\nfolium.Map.add_ee_layer = add_ee_layer",
"execution_count": 12,
"outputs": []
},
{
"metadata": {
"id": "6XqvR5-qQUpQ"
},
"cell_type": "markdown",
"source": "Define a function to display all of the cloud and cloud shadow components to an interactive Folium map. The input is an image collection where each image is the result of the `add_cld_shdw_mask` function defined previously."
},
{
"metadata": {
"id": "9yow4KRVZGST",
"trusted": true
},
"cell_type": "code",
"source": "def display_cloud_layers(col):\n # Mosaic the image collection.\n img = col.mosaic()\n\n # Subset layers and prepare them for display.\n clouds = img.select('clouds').selfMask()\n shadows = img.select('shadows').selfMask()\n dark_pixels = img.select('dark_pixels').selfMask()\n probability = img.select('probability')\n cloudmask = img.select('cloudmask').selfMask()\n cloud_transform = img.select('cloud_transform')\n\n # Create a folium map object.\n center = AOI.centroid(10).coordinates().reverse().getInfo()\n m = folium.Map(location=center, zoom_start=12)\n\n # Add layers to the folium map.\n m.add_ee_layer(img,\n {'bands': ['B4', 'B3', 'B2'], 'min': 0, 'max': 2500, 'gamma': 1.1},\n 'S2 image', True, 1, 9)\n m.add_ee_layer(probability,\n {'min': 0, 'max': 100},\n 'probability (cloud)', False, 1, 9)\n m.add_ee_layer(clouds,\n {'palette': 'e056fd'},\n 'clouds', False, 1, 9)\n m.add_ee_layer(cloud_transform,\n {'min': 0, 'max': 1, 'palette': ['white', 'black']},\n 'cloud_transform', False, 1, 9)\n m.add_ee_layer(dark_pixels,\n {'palette': 'orange'},\n 'dark_pixels', False, 1, 9)\n m.add_ee_layer(shadows, {'palette': 'yellow'},\n 'shadows', False, 1, 9)\n m.add_ee_layer(cloudmask, {'palette': 'orange'},\n 'cloudmask', True, 0.5, 9)\n\n # Add a layer control panel to the map.\n m.add_child(folium.LayerControl())\n\n # Display the map.\n display(m)",
"execution_count": 13,
"outputs": []
},
{
"metadata": {
"id": "-yteWco7sgfW"
},
"cell_type": "markdown",
"source": "### Display mask component layers\n\nMap the `add_cld_shdw_mask` function over the collection to add mask component bands to each image, then display the results.\n\nGive the system some time to render everything, it should take less than a minute."
},
{
"metadata": {
"id": "gvsULCqgdLHu",
"trusted": true
},
"cell_type": "code",
"source": "s2_sr_cld_col_eval_disp = s2_sr_cld_col_eval.map(add_cld_shdw_mask)\n\ndisplay_cloud_layers(s2_sr_cld_col_eval_disp)",
"execution_count": 14,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/plain": "<folium.folium.Map at 0x7ff8b4b459d0>",
"text/html": "<div style=\"width:100%;\"><div style=\"position:relative;width:100%;height:0;padding-bottom:60%;\"><iframe src=\"data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+CjxoZWFkPiAgICAKICAgIDxtZXRhIGh0dHAtZXF1aXY9ImNvbnRlbnQtdHlwZSIgY29udGVudD0idGV4dC9odG1sOyBjaGFyc2V0PVVURi04IiAvPgogICAgCiAgICAgICAgPHNjcmlwdD4KICAgICAgICAgICAgTF9OT19UT1VDSCA9IGZhbHNlOwogICAgICAgICAgICBMX0RJU0FCTEVfM0QgPSBmYWxzZTsKICAgICAgICA8L3NjcmlwdD4KICAgIAogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9ucG0vbGVhZmxldEAxLjYuMC9kaXN0L2xlYWZsZXQuanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY29kZS5qcXVlcnkuY29tL2pxdWVyeS0xLjEyLjQubWluLmpzIj48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJodHRwczovL21heGNkbi5ib290c3RyYXBjZG4uY29tL2Jvb3RzdHJhcC8zLjIuMC9qcy9ib290c3RyYXAubWluLmpzIj48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9MZWFmbGV0LmF3ZXNvbWUtbWFya2Vycy8yLjAuMi9sZWFmbGV0LmF3ZXNvbWUtbWFya2Vycy5qcyI+PC9zY3JpcHQ+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9ucG0vbGVhZmxldEAxLjYuMC9kaXN0L2xlYWZsZXQuY3NzIi8+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vbWF4Y2RuLmJvb3RzdHJhcGNkbi5jb20vYm9vdHN0cmFwLzMuMi4wL2Nzcy9ib290c3RyYXAubWluLmNzcyIvPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL21heGNkbi5ib290c3RyYXBjZG4uY29tL2Jvb3RzdHJhcC8zLjIuMC9jc3MvYm9vdHN0cmFwLXRoZW1lLm1pbi5jc3MiLz4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9tYXhjZG4uYm9vdHN0cmFwY2RuLmNvbS9mb250LWF3ZXNvbWUvNC42LjMvY3NzL2ZvbnQtYXdlc29tZS5taW4uY3NzIi8+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL0xlYWZsZXQuYXdlc29tZS1tYXJrZXJzLzIuMC4yL2xlYWZsZXQuYXdlc29tZS1tYXJrZXJzLmNzcyIvPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL3Jhd2Nkbi5naXRoYWNrLmNvbS9weXRob24tdmlzdWFsaXphdGlvbi9mb2xpdW0vbWFzdGVyL2ZvbGl1bS90ZW1wbGF0ZXMvbGVhZmxldC5hd2Vzb21lLnJvdGF0ZS5jc3MiLz4KICAgIDxzdHlsZT5odG1sLCBib2R5IHt3aWR0aDogMTAwJTtoZWlnaHQ6IDEwMCU7bWFyZ2luOiAwO3BhZGRpbmc6IDA7fTwvc3R5bGU+CiAgICA8c3R5bGU+I21hcCB7cG9zaXRpb246YWJzb2x1dGU7dG9wOjA7Ym90dG9tOjA7cmlnaHQ6MDtsZWZ0OjA7fTwvc3R5bGU+CiAgICAKICAgICAgICAgICAgPG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwKICAgICAgICAgICAgICAgIGluaXRpYWwtc2NhbGU9MS4wLCBtYXhpbXVtLXNjYWxlPTEuMCwgdXNlci1zY2FsYWJsZT1ubyIgLz4KICAgICAgICAgICAgPHN0eWxlPgogICAgICAgICAgICAgICAgI21hcF9kMTU0OWEzY2RlOTg0ZWZjYTU5MzhjZGY2Y2Y5OGE5YiB7CiAgICAgICAgICAgICAgICAgICAgcG9zaXRpb246IHJlbGF0aXZlOwogICAgICAgICAgICAgICAgICAgIHdpZHRoOiAxMDAuMCU7CiAgICAgICAgICAgICAgICAgICAgaGVpZ2h0OiAxMDAuMCU7CiAgICAgICAgICAgICAgICAgICAgbGVmdDogMC4wJTsKICAgICAgICAgICAgICAgICAgICB0b3A6IDAuMCU7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIDwvc3R5bGU+CiAgICAgICAgCjwvaGVhZD4KPGJvZHk+ICAgIAogICAgCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImZvbGl1bS1tYXAiIGlkPSJtYXBfZDE1NDlhM2NkZTk4NGVmY2E1OTM4Y2RmNmNmOThhOWIiID48L2Rpdj4KICAgICAgICAKPC9ib2R5Pgo8c2NyaXB0PiAgICAKICAgIAogICAgICAgICAgICB2YXIgbWFwX2QxNTQ5YTNjZGU5ODRlZmNhNTkzOGNkZjZjZjk4YTliID0gTC5tYXAoCiAgICAgICAgICAgICAgICAibWFwX2QxNTQ5YTNjZGU5ODRlZmNhNTkzOGNkZjZjZjk4YTliIiwKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBjZW50ZXI6IFs0NS43MDA5OTk5OTk5OTk5OSwgLTEyMi4yNjkwMDAwMDAwMDAwMl0sCiAgICAgICAgICAgICAgICAgICAgY3JzOiBMLkNSUy5FUFNHMzg1NywKICAgICAgICAgICAgICAgICAgICB6b29tOiAxMiwKICAgICAgICAgICAgICAgICAgICB6b29tQ29udHJvbDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICBwcmVmZXJDYW52YXM6IGZhbHNlLAogICAgICAgICAgICAgICAgfQogICAgICAgICAgICApOwoKICAgICAgICAgICAgCgogICAgICAgIAogICAgCiAgICAgICAgICAgIHZhciB0aWxlX2xheWVyXzA0ZTM4NGU1N2IzZTRhODM5MzRmZTNjNmIxZDVlNzczID0gTC50aWxlTGF5ZXIoCiAgICAgICAgICAgICAgICAiaHR0cHM6Ly97c30udGlsZS5vcGVuc3RyZWV0bWFwLm9yZy97en0ve3h9L3t5fS5wbmciLAogICAgICAgICAgICAgICAgeyJhdHRyaWJ1dGlvbiI6ICJEYXRhIGJ5IFx1MDAyNmNvcHk7IFx1MDAzY2EgaHJlZj1cImh0dHA6Ly9vcGVuc3RyZWV0bWFwLm9yZ1wiXHUwMDNlT3BlblN0cmVldE1hcFx1MDAzYy9hXHUwMDNlLCB1bmRlciBcdTAwM2NhIGhyZWY9XCJodHRwOi8vd3d3Lm9wZW5zdHJlZXRtYXAub3JnL2NvcHlyaWdodFwiXHUwMDNlT0RiTFx1MDAzYy9hXHUwMDNlLiIsICJkZXRlY3RSZXRpbmEiOiBmYWxzZSwgIm1heE5hdGl2ZVpvb20iOiAxOCwgIm1heFpvb20iOiAxOCwgIm1pblpvb20iOiAwLCAibm9XcmFwIjogZmFsc2UsICJvcGFjaXR5IjogMSwgInN1YmRvbWFpbnMiOiAiYWJjIiwgInRtcyI6IGZhbHNlfQogICAgICAgICAgICApLmFkZFRvKG1hcF9kMTU0OWEzY2RlOTg0ZWZjYTU5MzhjZGY2Y2Y5OGE5Yik7CiAgICAgICAgCiAgICAKICAgICAgICAgICAgdmFyIHRpbGVfbGF5ZXJfZjg0YWJlMTU2N2I3NDFjNGJhNGUwZmQ2ZTdhNDk3ZGMgPSBMLnRpbGVMYXllcigKICAgICAgICAgICAgICAgICJodHRwczovL2VhcnRoZW5naW5lLmdvb2dsZWFwaXMuY29tL3YxYWxwaGEvcHJvamVjdHMvZWFydGhlbmdpbmUtbGVnYWN5L21hcHMvNWI0MWJhNTRmNjJmOGRlMWNlZDcwYTZjNjIxOGUzYzQtYjM4NTJiYWJiMWNmMzkzZDZhNjk4MGNiMzVlYmRjN2IvdGlsZXMve3p9L3t4fS97eX0iLAogICAgICAgICAgICAgICAgeyJhdHRyaWJ1dGlvbiI6ICJNYXAgRGF0YSBcdTAwMjZjb3B5OyBcdTAwM2NhIGhyZWY9XCJodHRwczovL2VhcnRoZW5naW5lLmdvb2dsZS5jb20vXCJcdTAwM2VHb29nbGUgRWFydGggRW5naW5lXHUwMDNjL2FcdTAwM2UiLCAiZGV0ZWN0UmV0aW5hIjogZmFsc2UsICJtYXhOYXRpdmVab29tIjogMTgsICJtYXhab29tIjogMTgsICJtaW5ab29tIjogOSwgIm5vV3JhcCI6IGZhbHNlLCAib3BhY2l0eSI6IDEsICJzdWJkb21haW5zIjogImFiYyIsICJ0bXMiOiBmYWxzZX0KICAgICAgICAgICAgKS5hZGRUbyhtYXBfZDE1NDlhM2NkZTk4NGVmY2E1OTM4Y2RmNmNmOThhOWIpOwogICAgICAgIAogICAgCiAgICAgICAgICAgIHZhciB0aWxlX2xheWVyXzI0YjcxMjI2MmQ0YzQ2NDQ4NDMyNjNmNmRmMDIwZDgwID0gTC50aWxlTGF5ZXIoCiAgICAgICAgICAgICAgICAiaHR0cHM6Ly9lYXJ0aGVuZ2luZS5nb29nbGVhcGlzLmNvbS92MWFscGhhL3Byb2plY3RzL2VhcnRoZW5naW5lLWxlZ2FjeS9tYXBzLzgwNWVmODBmOTgxY2I0MGM4YWFhZDAwNzdmNDhkNTdmLTBhNjYyNzk0OWEzMzU5ZDdlNzcyZTQ3MzljMzc0NzQ3L3RpbGVzL3t6fS97eH0ve3l9IiwKICAgICAgICAgICAgICAgIHsiYXR0cmlidXRpb24iOiAiTWFwIERhdGEgXHUwMDI2Y29weTsgXHUwMDNjYSBocmVmPVwiaHR0cHM6Ly9lYXJ0aGVuZ2luZS5nb29nbGUuY29tL1wiXHUwMDNlR29vZ2xlIEVhcnRoIEVuZ2luZVx1MDAzYy9hXHUwMDNlIiwgImRldGVjdFJldGluYSI6IGZhbHNlLCAibWF4TmF0aXZlWm9vbSI6IDE4LCAibWF4Wm9vbSI6IDE4LCAibWluWm9vbSI6IDksICJub1dyYXAiOiBmYWxzZSwgIm9wYWNpdHkiOiAxLCAic3ViZG9tYWlucyI6ICJhYmMiLCAidG1zIjogZmFsc2V9CiAgICAgICAgICAgICkuYWRkVG8obWFwX2QxNTQ5YTNjZGU5ODRlZmNhNTkzOGNkZjZjZjk4YTliKTsKICAgICAgICAKICAgIAogICAgICAgICAgICB2YXIgdGlsZV9sYXllcl85NTJiMWVhYmM0ZTg0MDg1OTc4ZGVkNmJjOGRjYmI0ZSA9IEwudGlsZUxheWVyKAogICAgICAgICAgICAgICAgImh0dHBzOi8vZWFydGhlbmdpbmUuZ29vZ2xlYXBpcy5jb20vdjFhbHBoYS9wcm9qZWN0cy9lYXJ0aGVuZ2luZS1sZWdhY3kvbWFwcy83YTczYzA3YjJjNDBhMzAyMGM4OGExOWFkMTExZDlmNy1hZWE1YzAzMmEwYWE5ODdjMDU1ZTRkMmU1MjVhNGIzMi90aWxlcy97en0ve3h9L3t5fSIsCiAgICAgICAgICAgICAgICB7ImF0dHJpYnV0aW9uIjogIk1hcCBEYXRhIFx1MDAyNmNvcHk7IFx1MDAzY2EgaHJlZj1cImh0dHBzOi8vZWFydGhlbmdpbmUuZ29vZ2xlLmNvbS9cIlx1MDAzZUdvb2dsZSBFYXJ0aCBFbmdpbmVcdTAwM2MvYVx1MDAzZSIsICJkZXRlY3RSZXRpbmEiOiBmYWxzZSwgIm1heE5hdGl2ZVpvb20iOiAxOCwgIm1heFpvb20iOiAxOCwgIm1pblpvb20iOiA5LCAibm9XcmFwIjogZmFsc2UsICJvcGFjaXR5IjogMSwgInN1YmRvbWFpbnMiOiAiYWJjIiwgInRtcyI6IGZhbHNlfQogICAgICAgICAgICApLmFkZFRvKG1hcF9kMTU0OWEzY2RlOTg0ZWZjYTU5MzhjZGY2Y2Y5OGE5Yik7CiAgICAgICAgCiAgICAKICAgICAgICAgICAgdmFyIHRpbGVfbGF5ZXJfOTY4MjUzMDQ4YzdlNGVjZWEzYWM3NjY1ZjhlMDk2NzEgPSBMLnRpbGVMYXllcigKICAgICAgICAgICAgICAgICJodHRwczovL2VhcnRoZW5naW5lLmdvb2dsZWFwaXMuY29tL3YxYWxwaGEvcHJvamVjdHMvZWFydGhlbmdpbmUtbGVnYWN5L21hcHMvNmE0YTY5NWRmNWQxNTQwNWEyMDkxNDI5YmYwNWRkNzMtNjJhMmUxNDZlY2IzNmVlNzYyYjAwOThiNTdjNGZiNTcvdGlsZXMve3p9L3t4fS97eX0iLAogICAgICAgICAgICAgICAgeyJhdHRyaWJ1dGlvbiI6ICJNYXAgRGF0YSBcdTAwMjZjb3B5OyBcdTAwM2NhIGhyZWY9XCJodHRwczovL2VhcnRoZW5naW5lLmdvb2dsZS5jb20vXCJcdTAwM2VHb29nbGUgRWFydGggRW5naW5lXHUwMDNjL2FcdTAwM2UiLCAiZGV0ZWN0UmV0aW5hIjogZmFsc2UsICJtYXhOYXRpdmVab29tIjogMTgsICJtYXhab29tIjogMTgsICJtaW5ab29tIjogOSwgIm5vV3JhcCI6IGZhbHNlLCAib3BhY2l0eSI6IDEsICJzdWJkb21haW5zIjogImFiYyIsICJ0bXMiOiBmYWxzZX0KICAgICAgICAgICAgKS5hZGRUbyhtYXBfZDE1NDlhM2NkZTk4NGVmY2E1OTM4Y2RmNmNmOThhOWIpOwogICAgICAgIAogICAgCiAgICAgICAgICAgIHZhciB0aWxlX2xheWVyX2RmZGY1Y2YyOTYzZDQ1YTliOTFiY2UwNzk0MTkwOWUyID0gTC50aWxlTGF5ZXIoCiAgICAgICAgICAgICAgICAiaHR0cHM6Ly9lYXJ0aGVuZ2luZS5nb29nbGVhcGlzLmNvbS92MWFscGhhL3Byb2plY3RzL2VhcnRoZW5naW5lLWxlZ2FjeS9tYXBzLzk2M2Q1OTYyNThiOTFhMTYyOTkwN2U2YTI5NTZlNzFhLTZlM2Q3MjYxMTdmMjQyMDY3Y2Y2MGNlOTEyYmFmMjc5L3RpbGVzL3t6fS97eH0ve3l9IiwKICAgICAgICAgICAgICAgIHsiYXR0cmlidXRpb24iOiAiTWFwIERhdGEgXHUwMDI2Y29weTsgXHUwMDNjYSBocmVmPVwiaHR0cHM6Ly9lYXJ0aGVuZ2luZS5nb29nbGUuY29tL1wiXHUwMDNlR29vZ2xlIEVhcnRoIEVuZ2luZVx1MDAzYy9hXHUwMDNlIiwgImRldGVjdFJldGluYSI6IGZhbHNlLCAibWF4TmF0aXZlWm9vbSI6IDE4LCAibWF4Wm9vbSI6IDE4LCAibWluWm9vbSI6IDksICJub1dyYXAiOiBmYWxzZSwgIm9wYWNpdHkiOiAxLCAic3ViZG9tYWlucyI6ICJhYmMiLCAidG1zIjogZmFsc2V9CiAgICAgICAgICAgICkuYWRkVG8obWFwX2QxNTQ5YTNjZGU5ODRlZmNhNTkzOGNkZjZjZjk4YTliKTsKICAgICAgICAKICAgIAogICAgICAgICAgICB2YXIgdGlsZV9sYXllcl82NTg5YjJmZjgwNGY0MDMxOWM2NzU2Njk4ZjkxZGUzZSA9IEwudGlsZUxheWVyKAogICAgICAgICAgICAgICAgImh0dHBzOi8vZWFydGhlbmdpbmUuZ29vZ2xlYXBpcy5jb20vdjFhbHBoYS9wcm9qZWN0cy9lYXJ0aGVuZ2luZS1sZWdhY3kvbWFwcy85MWVkZGRhNGU2OTgzMWJmNmNjNWY5YTA2ZmMzOWIzOS03NTg5NjA3OTJmOGNlNjczYjgzYTM5NjU5YmRmNzA0NC90aWxlcy97en0ve3h9L3t5fSIsCiAgICAgICAgICAgICAgICB7ImF0dHJpYnV0aW9uIjogIk1hcCBEYXRhIFx1MDAyNmNvcHk7IFx1MDAzY2EgaHJlZj1cImh0dHBzOi8vZWFydGhlbmdpbmUuZ29vZ2xlLmNvbS9cIlx1MDAzZUdvb2dsZSBFYXJ0aCBFbmdpbmVcdTAwM2MvYVx1MDAzZSIsICJkZXRlY3RSZXRpbmEiOiBmYWxzZSwgIm1heE5hdGl2ZVpvb20iOiAxOCwgIm1heFpvb20iOiAxOCwgIm1pblpvb20iOiA5LCAibm9XcmFwIjogZmFsc2UsICJvcGFjaXR5IjogMSwgInN1YmRvbWFpbnMiOiAiYWJjIiwgInRtcyI6IGZhbHNlfQogICAgICAgICAgICApLmFkZFRvKG1hcF9kMTU0OWEzY2RlOTg0ZWZjYTU5MzhjZGY2Y2Y5OGE5Yik7CiAgICAgICAgCiAgICAKICAgICAgICAgICAgdmFyIHRpbGVfbGF5ZXJfOTlhOTYwNmEzZWFhNDg1NWIyMTI3NmFkZGU0MDk3YzIgPSBMLnRpbGVMYXllcigKICAgICAgICAgICAgICAgICJodHRwczovL2VhcnRoZW5naW5lLmdvb2dsZWFwaXMuY29tL3YxYWxwaGEvcHJvamVjdHMvZWFydGhlbmdpbmUtbGVnYWN5L21hcHMvOGFhOWM4YTRjNzJiMDRhZmI4YWU2YjljNDY2NjAzN2YtYWFmZTdhOTk1Yzg2NDcwZjQwYzIyYTA4NjkyZWQyMzkvdGlsZXMve3p9L3t4fS97eX0iLAogICAgICAgICAgICAgICAgeyJhdHRyaWJ1dGlvbiI6ICJNYXAgRGF0YSBcdTAwMjZjb3B5OyBcdTAwM2NhIGhyZWY9XCJodHRwczovL2VhcnRoZW5naW5lLmdvb2dsZS5jb20vXCJcdTAwM2VHb29nbGUgRWFydGggRW5naW5lXHUwMDNjL2FcdTAwM2UiLCAiZGV0ZWN0UmV0aW5hIjogZmFsc2UsICJtYXhOYXRpdmVab29tIjogMTgsICJtYXhab29tIjogMTgsICJtaW5ab29tIjogOSwgIm5vV3JhcCI6IGZhbHNlLCAib3BhY2l0eSI6IDAuNSwgInN1YmRvbWFpbnMiOiAiYWJjIiwgInRtcyI6IGZhbHNlfQogICAgICAgICAgICApLmFkZFRvKG1hcF9kMTU0OWEzY2RlOTg0ZWZjYTU5MzhjZGY2Y2Y5OGE5Yik7CiAgICAgICAgCiAgICAKICAgICAgICAgICAgdmFyIGxheWVyX2NvbnRyb2xfZGJkMjkxY2VjNTVmNDYxNGI1NjMyMWI5ODE1YjQ2Y2QgPSB7CiAgICAgICAgICAgICAgICBiYXNlX2xheWVycyA6IHsKICAgICAgICAgICAgICAgICAgICAib3BlbnN0cmVldG1hcCIgOiB0aWxlX2xheWVyXzA0ZTM4NGU1N2IzZTRhODM5MzRmZTNjNmIxZDVlNzczLAogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgIG92ZXJsYXlzIDogIHsKICAgICAgICAgICAgICAgICAgICAiUzIgaW1hZ2UiIDogdGlsZV9sYXllcl9mODRhYmUxNTY3Yjc0MWM0YmE0ZTBmZDZlN2E0OTdkYywKICAgICAgICAgICAgICAgICAgICAicHJvYmFiaWxpdHkgKGNsb3VkKSIgOiB0aWxlX2xheWVyXzI0YjcxMjI2MmQ0YzQ2NDQ4NDMyNjNmNmRmMDIwZDgwLAogICAgICAgICAgICAgICAgICAgICJjbG91ZHMiIDogdGlsZV9sYXllcl85NTJiMWVhYmM0ZTg0MDg1OTc4ZGVkNmJjOGRjYmI0ZSwKICAgICAgICAgICAgICAgICAgICAiY2xvdWRfdHJhbnNmb3JtIiA6IHRpbGVfbGF5ZXJfOTY4MjUzMDQ4YzdlNGVjZWEzYWM3NjY1ZjhlMDk2NzEsCiAgICAgICAgICAgICAgICAgICAgImRhcmtfcGl4ZWxzIiA6IHRpbGVfbGF5ZXJfZGZkZjVjZjI5NjNkNDVhOWI5MWJjZTA3OTQxOTA5ZTIsCiAgICAgICAgICAgICAgICAgICAgInNoYWRvd3MiIDogdGlsZV9sYXllcl82NTg5YjJmZjgwNGY0MDMxOWM2NzU2Njk4ZjkxZGUzZSwKICAgICAgICAgICAgICAgICAgICAiY2xvdWRtYXNrIiA6IHRpbGVfbGF5ZXJfOTlhOTYwNmEzZWFhNDg1NWIyMTI3NmFkZGU0MDk3YzIsCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICB9OwogICAgICAgICAgICBMLmNvbnRyb2wubGF5ZXJzKAogICAgICAgICAgICAgICAgbGF5ZXJfY29udHJvbF9kYmQyOTFjZWM1NWY0NjE0YjU2MzIxYjk4MTViNDZjZC5iYXNlX2xheWVycywKICAgICAgICAgICAgICAgIGxheWVyX2NvbnRyb2xfZGJkMjkxY2VjNTVmNDYxNGI1NjMyMWI5ODE1YjQ2Y2Qub3ZlcmxheXMsCiAgICAgICAgICAgICAgICB7ImF1dG9aSW5kZXgiOiB0cnVlLCAiY29sbGFwc2VkIjogdHJ1ZSwgInBvc2l0aW9uIjogInRvcHJpZ2h0In0KICAgICAgICAgICAgKS5hZGRUbyhtYXBfZDE1NDlhM2NkZTk4NGVmY2E1OTM4Y2RmNmNmOThhOWIpOwogICAgICAgICAgICB0aWxlX2xheWVyXzI0YjcxMjI2MmQ0YzQ2NDQ4NDMyNjNmNmRmMDIwZDgwLnJlbW92ZSgpOwogICAgICAgICAgICB0aWxlX2xheWVyXzk1MmIxZWFiYzRlODQwODU5NzhkZWQ2YmM4ZGNiYjRlLnJlbW92ZSgpOwogICAgICAgICAgICB0aWxlX2xheWVyXzk2ODI1MzA0OGM3ZTRlY2VhM2FjNzY2NWY4ZTA5NjcxLnJlbW92ZSgpOwogICAgICAgICAgICB0aWxlX2xheWVyX2RmZGY1Y2YyOTYzZDQ1YTliOTFiY2UwNzk0MTkwOWUyLnJlbW92ZSgpOwogICAgICAgICAgICB0aWxlX2xheWVyXzY1ODliMmZmODA0ZjQwMzE5YzY3NTY2OThmOTFkZTNlLnJlbW92ZSgpOwogICAgICAgIAo8L3NjcmlwdD4=\" style=\"position:absolute;width:100%;height:100%;left:0;top:0;border:none !important;\" allowfullscreen webkitallowfullscreen mozallowfullscreen></iframe></div></div>"
},
"metadata": {}
}
]
},
{
"metadata": {
"id": "HZAd5ZHWWZed"
},
"cell_type": "markdown",
"source": "### Evaluate mask component layers\n\nIn the above map, use the layer control panel in the upper right corner to toggle layers on and off; layer names are the same as band names, for easy code referral. Note that the layers have a minimum zoom level of 9 to avoid resource issues that can occur when visualizing layers that depend on the `ee.Image.reproject` function (used during cloud shadow project and mask dilation).\n\nTry changing the above `CLD_PRB_THRESH`, `NIR_DRK_THRESH`, `CLD_PRJ_DIST`, and `BUFFER` input variables and rerunning the previous cell to see how the results change. Find a good set of values for a given overpass and then try the procedure with a new overpass with different cloud conditions (this [S2 SR image browser app](https://showcase.earthengine.app/view/s2-sr-browser-s2cloudless-nb) is handy for quickly identifying images and determining image collection filter criteria). Try to identify a set of parameter values that balances cloud/cloud shadow commission and omission error for a range of cloud types. In the next section, we'll use the values to actually apply the mask to generate a cloud-free composite for 2020."
},
{
"metadata": {
"id": "7iR3d7HCYf8j"
},
"cell_type": "markdown",
"source": "## Apply cloud and cloud shadow mask\n\nIn this section we'll generate a cloud-free composite for the same region as above that represents mean reflectance for July and August, 2020."
},
{
"metadata": {
"id": "V1lnub3LNoiq"
},
"cell_type": "markdown",
"source": "### Define collection filter and cloud mask parameters\n\nWe'll redefine the parameters to be a little more aggressive, i.e. decrease the cloud probability threshold, increase the cloud projection distance, and increase the buffer. These changes will increase cloud commission error (mask out some clear pixels), but since we will be compositing images from three months, there should be plenty of observations to complete the mosaic."
},
{
"metadata": {
"id": "H_eDsLZ5Yzxh",
"trusted": true
},
"cell_type": "code",
"source": "AOI = ee.Geometry.Point(-122.269, 45.701)\nSTART_DATE = '2020-06-01'\nEND_DATE = '2020-09-01'\nCLOUD_FILTER = 60\nCLD_PRB_THRESH = 40\nNIR_DRK_THRESH = 0.15\nCLD_PRJ_DIST = 2\nBUFFER = 100",
"execution_count": 15,
"outputs": []
},
{
"metadata": {
"id": "R6iuJ2FybAc_"
},
"cell_type": "markdown",
"source": "### Build a Sentinel-2 collection\n\nReassemble the S2-cloudless collection since the collection filter parameters have changed."
},
{
"metadata": {
"id": "AIk8xa0ImRTS",
"trusted": true
},
"cell_type": "code",
"source": "s2_sr_cld_col = get_s2_sr_cld_col(AOI, START_DATE, END_DATE)",
"execution_count": 16,
"outputs": []
},
{
"metadata": {
"id": "iuJ-Ml9taz-i"
},
"cell_type": "markdown",
"source": "### Define cloud mask application function\n\nDefine a function to apply the cloud mask to each image in the collection."
},
{
"metadata": {
"id": "VyR9JYxbbJJQ",
"trusted": true
},
"cell_type": "code",
"source": "def apply_cld_shdw_mask(img):\n # Subset the cloudmask band and invert it so clouds/shadow are 0, else 1.\n not_cld_shdw = img.select('cloudmask').Not()\n\n # Subset reflectance bands and update their masks, return the result.\n return img.select('B.*').updateMask(not_cld_shdw)",
"execution_count": 17,
"outputs": []
},
{
"metadata": {
"id": "8dT3EWPqcOkA"
},
"cell_type": "markdown",
"source": "### Process the collection\n\nAdd cloud and cloud shadow component bands to each image and then apply the mask to each image. Reduce the collection by median (in your application, you might consider using medoid reduction to build a composite from actual data values, instead of per-band statistics)."
},
{
"metadata": {
"id": "G2XogQYycO53",
"trusted": true
},
"cell_type": "code",
"source": "s2_sr_median = (s2_sr_cld_col.map(add_cld_shdw_mask)\n .map(apply_cld_shdw_mask)\n .median())",
"execution_count": 18,
"outputs": []
},
{
"metadata": {
"id": "nYS9EV_Mc-2_"
},
"cell_type": "markdown",
"source": "### Display the cloud-free composite\n\nDisplay the results. Be patient while the map renders, it may take a minute; [`ee.Image.reproject`](https://developers.google.com/earth-engine/guides/projections#reprojecting) is forcing computations to happen at 100 and 20 m scales (i.e. it is not relying on appropriate pyramid level [scales for analysis](https://developers.google.com/earth-engine/guides/scale#scale-of-analysis)). The issues with `ee.Image.reproject` being resource-intensive in this case are mostly confined to interactive map viewing. Batch image [exports](https://developers.google.com/earth-engine/guides/exporting) and table reduction exports where the `scale` parameter is set to typical Sentinel-2 scales (10-60 m) are less affected.\n\n"
},
{
"metadata": {
"id": "hMObmv_tdLaX",
"trusted": true
},
"cell_type": "code",
"source": "# Create a folium map object.\ncenter = AOI.centroid(10).coordinates().reverse().getInfo()\n# m = folium.Map(location=center, zoom_start=12)\n\n# Add layers to the folium map.\nMap.addLayer(s2_sr_median,\n {'bands': ['B4', 'B3', 'B2'], 'min': 0, 'max': 2500, 'gamma': 1.1},\n 'S2 cloud-free mosaic', True, 1)\nMap.setCenter(center[1], center[0], 12)\n\n# Add a layer control panel to the map.\n# m.add_child(folium.LayerControl())\n\n# Display the map.\n# display(m)",
"execution_count": 23,
"outputs": []
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "Map",
"execution_count": 21,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/plain": "Map(center=[40, -100], controls=(WidgetControl(options=['position', 'transparent_bg'], widget=HBox(children=(T…",
"application/vnd.jupyter.widget-view+json": {
"version_major": 2,
"version_minor": 0,
"model_id": "0c0d2e0adbad4f6cb914e82c45b8e589"
}
},
"metadata": {}
}
]
},
{
"metadata": {
"id": "PuqD4vNyo-aF"
},
"cell_type": "markdown",
"source": "Hopefully you now have a good sense for Sentinel-2 cloud masking in the cloud 😉 with Earth Engine."
}
],
"metadata": {
"colab": {
"name": "Sentinel-2 Cloud Masking with s2cloudless",
"provenance": [],
"collapsed_sections": []
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3",
"language": "python"
},
"hide_input": false,
"varInspector": {
"window_display": false,
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"library": "var_list.py",
"delete_cmd_prefix": "del ",
"delete_cmd_postfix": "",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"library": "var_list.r",
"delete_cmd_prefix": "rm(",
"delete_cmd_postfix": ") ",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
]
},
"toc": {
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"base_numbering": 1,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
},
"language_info": {
"name": "python",
"version": "3.8.5",
"mimetype": "text/x-python",
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"pygments_lexer": "ipython3",
"nbconvert_exporter": "python",
"file_extension": ".py"
},
"gist": {
"id": "",
"data": {
"description": "s2.ipynb",
"public": true
}
}
},
"nbformat": 4,
"nbformat_minor": 1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment