Scenes


Working with Scenes


Scenes is a tool in the Descartes Labs platform to search and access imagery over a wide variety of data sources. Below we show how to define an area of interest, search for imagery, download the image, and start doing analysis. First let's import the Descartes Labs library and define a place of interest.

You can run the following cells using Shift-Enter.

In [1]:
# keep logging quiet
import logging
logging.getLogger().setLevel(logging.INFO)
logging.captureWarnings(True)
In [2]:
# import packages
import descarteslabs as dl
import numpy as np

Our area of interest is Folsom Lake, a large resevoir in northern California. We can define an area of interest by specifying a GeoJSON feature object.

In [3]:
feature = {
    "type":"Feature",
    "geometry":{
    "type":"Polygon",
        "coordinates":[[
            [-121.18599701346828, 38.693120375759946],
            [-121.08821867639196, 38.693120375759946],
            [-121.08821867639196, 38.77282238770522],
            [-121.18599701346828, 38.77282238770522],
            [-121.18599701346828, 38.693120375759946]
        ]]
    },
    "properties": {}
} 
fc = {"type": "FeatureCollection", "features": [feature], "properties": {}}
In [4]:
from ipyleaflet import Map, GeoJSON
m = Map(center=(38.733120, -121.1221), zoom=12)
m.add_layer(GeoJSON(data=fc))
m

If we want to find imagery over the region we can query for it using the scenes.search method. The search function will return a SceneCollection object, a collection of metadata associated with images that match our query. It also returns a GeoContext which has metadata about the region of interest, projections, and scale.

In [5]:
scenes, ctx = dl.scenes.search(
    aoi=feature,
    products='sentinel-2:L1C',
    start_datetime='2019-07-31',
    end_datetime='2019-08-05'
)
In [6]:
scenes
Out[6]:
SceneCollection of 2 scenes
  * Dates: Aug 03, 2019 to Aug 03, 2019
  * Products: sentinel-2:L1C: 2
In [7]:
ctx
Out[7]:
AOI(geometry=<shapely.geom...x7f3f2b58ea58>,
    resolution=10,
    crs='EPSG:32610',
    align_pixels=True,
    bounds=(-121.18599701346828, 38.693120375759946, -121.08821867639196, 38.77282238770522),
    bounds_crs='EPSG:4326',
    shape=None)

Individual scenes in a SceneCollection can be accessed by indexing the collection just as if the data was a list. Inspecting a scene displays a detailed list of properties including information about the sensor, bands, and timestamps associated with the image.

In [8]:
scenes[0]
Out[8]:
Scene "sentinel-2:L1C:2019-08-03_10SFH_99_S2B_v1"
  * Product: "sentinel-2:L1C"
  * CRS: "EPSG:32610"
  * Date: Sat Aug  3 19:03:59 2019
  * Bands:
    * derived:bai: UInt16, [0, 65535] -> [-1.0, 1.0]
    * derived:evi: UInt16, [0, 65535] -> [-1.0, 1.0]
    * derived:nbr: UInt16, [0, 65535] -> [-1.0, 1.0]
    * derived:ndvi: UInt16, [0, 65535] -> [-1.0, 1.0]
    * derived:ndwi: UInt16, [0, 65535] -> [-1.0, 1.0]
    * derived:ndwi1: UInt16, [0, 65535] -> [-1.0, 1.0]
    * derived:ndwi2: UInt16, [0, 65535] -> [-1.0, 1.0]
    * derived:rsqrt: Float64, [0, 1000] -> [0.0, 1.0]
    * derived:visual_cloud_mask: UInt16, [0, 1] -> None
    * alpha: 10 meters, UInt16, [0, 1]
    * blue: 10 meters, UInt16, [0, 10000] -> [0.0, 1.0] in units "TOAR"
    * bright-mask: 10 meters, UInt16, [0, 1]
    * cirrus: 60 meters, UInt16, [0, 10000] -> [0.0, 1.0] in units "TOAR"
    * cirrus-cloud-mask: 60 meters, UInt16, [0, 1]
    * cloud-mask: 60 meters, UInt16, [0, 1]
    * coastal-aerosol: 60 meters, UInt16, [0, 10000] -> [0.0, 1.0] in units "TOAR"
    * green: 10 meters, UInt16, [0, 10000] -> [0.0, 1.0] in units "TOAR"
    * nir: 10 meters, UInt16, [0, 10000] -> [0.0, 1.0] in units "TOAR"
    * opaque-cloud-mask: 60 meters, UInt16, [0, 1]
    * red: 10 meters, UInt16, [0, 10000] -> [0.0, 1.0] in units "TOAR"
    * red-edge: 20 meters, UInt16, [0, 10000] -> [0.0, 1.0] in units "TOAR"
    * red-edge-2: 20 meters, UInt16, [0, 10000] -> [0.0, 1.0] in units "TOAR"
    * red-edge-3: 20 meters, UInt16, [0, 10000] -> [0.0, 1.0] in units "TOAR"
    * red-edge-4: 20 meters, UInt16, [0, 10000] -> [0.0, 1.0] in units "TOAR"
    * swir1: 20 meters, UInt16, [0, 10000] -> [0.0, 1.0] in units "TOAR"
    * swir2: 20 meters, UInt16, [0, 10000] -> [0.0, 1.0] in units "TOAR"
    * water-vapor: 60 meters, UInt16, [0, 10000] -> [0.0, 1.0] in units "TOAR"

We can even get individual properties from a scene under its property field, which can be accessed like an attribute or a dictionary.

In [9]:
scenes[0].properties.date
# or scenes[0].properties['date']
Out[9]:
datetime.datetime(2019, 8, 3, 19, 3, 59, 316807)

Although we have just queried for matching images, we have not downloaded anything yet. Getting the pixels from any given scene is as easy as calling the ndarray method on a scene. The function returns an numpy array that is then ready to be used for further analysis. We need to assign a resolution to the GeoContext and choose which bands we want.

In [10]:
ctx = ctx.assign(resolution=10)
In [11]:
a = scenes[0].ndarray(
    bands='red green blue',
    ctx=ctx
)
In [12]:
a.shape
Out[12]:
(3, 903, 833)
In [13]:
%matplotlib inline
dl.scenes.display(a)