Processing#
easysnowdata.processing#
Pure processing functions (design contract §2.6): xarray in, xarray out, no I/O.
masks— Sentinel-2 SCL and HLS Fmask masks, nodata maskingoptical— baseline harmonization, metadata-driven scale/offset, the UDM2 mask decodersar— dB conversion, border-noise removal, slope/aspect and the local incidence anglesnow— snow-product class tables and the SNODAS flat-file readerwateryear— vectorized water-year helperscategorical— CF flag attributes for categorical products (the contract’s replacement forclass_infodicts)contract— the output contract (§2.5): CRS via both accessors, dim names, nodata policy, provenance attrs
Attach |
|
|
|
Mask obj to NaN where Fmask flags are set ( |
|
Mask obj to NaN where the SCL class is in remove ( |
|
Day of the water year, 1-indexed (1 October = 1 in the northern hemisphere). |
|
|
|
Decode a PlanetScope UDM2 mask into named variables. |
|
Boolean mask that is |
|
Read CF flag attributes into a DataFrame with |
|
The aerosol level 0–3 encoded in Fmask bits 6–7. |
|
The value (0/1) of bit in each Fmask pixel. |
|
Boolean mask that is |
|
Convert the legacy |
|
Undo the post-2022-01-25 Sentinel-2 L2A offset so all dates share one baseline. |
|
|
|
Local incidence angle from a DEM and the ellipsoidal incidence angle. |
|
Radar look azimuth in degrees from the platform heading. |
|
Replace the nodata value with NaN (converting integers to float). |
|
CF attributes for an NDSI byte: percentages plus the sentinel meanings. |
|
Parse a NOHRSC |
|
Mask falsely low backscatter (border noise) in linear-power data. |
|
Apply |
|
Boolean mask that is |
|
Attach CF flag attributes to a categorical DataArray. |
|
Slope and aspect of a projected DEM, by central differences. |
|
The value (0/1) of one UDM1 bit ( |
|
The water year of each time as an integer (calendar year the WY ends in for the north). |
|
The first day of the water year containing each time (same container type as the input). |
easysnowdata.processing.categorical#
CF flag attributes for categorical products.
The design contract (§2.5) forbids Python objects in .attrs: a categorical
variable carries flag_values (list of ints), flag_meanings (one
space-separated string, blanks inside a meaning become underscores) and
flag_colors (space-separated hex strings). These helpers write and read
that form; easysnowdata.plotting turns it into colormaps and legends.
Normalise a class name to a CF |
easysnowdata.processing.contract#
Output-contract helpers (design contract §2.5), shared by every theme loader.
Pure functions: they rename dimensions, write the CRS with both the rio
and odc accessors, mask nodata the way the contract prescribes
(categorical keeps the sentinel with rio.nodata set; continuous is
NaN-masked with encoded_nodata preserved) and stamp the standard
provenance attributes (source, source_url, product_id,
data_citation, license, easysnowdata_version). No I/O.
Give a loader's |
|
Rename spatial dims to the contract: |
|
Write crs so that both |
|
NaN-mask a continuous variable and keep the encoded nodata value. |
|
Keep the source sentinel and record it as |
|
The standard provenance attrs for a catalog product loaded via source. |
|
Apply the catalog |
|
Bring a loaded object onto the output contract. |
|
Bring a loaded GeoDataFrame onto the output contract (§2.5). |
easysnowdata.processing.masks#
Quality masks: Sentinel-2 SCL classes, HLS Fmask bits, nodata.
easysnowdata.processing.optical#
Optical processing: Sentinel-2 baseline harmonization, metadata-driven scale/offset, and the PlanetScope UDM2 mask decoder. Pure functions.
Band arithmetic is deliberately not wrapped here. A normalized difference is
one line of xarray, and hiding it behind ndsi(ds) obscured which bands
were being used and what happened to negative reflectance:
ndsi_da = (s2_ds["green"] - s2_ds["swir16"]) / (s2_ds["green"] + s2_ds["swir16"])
rgb_da = s2_ds[["red", "green", "blue"]].to_array("band").clip(0, 0.3) / 0.3
rgb_da.isel(time=0).plot.imshow(rgb="band")
The gallery examples spell these out every time they are used.
easysnowdata.processing.sar#
SAR backscatter helpers: dB conversion and Sentinel-1 border-noise removal.
easysnowdata.processing.snow#
Snow-product processing: class tables and the SNODAS flat-file reader.
Pure functions (design contract §2.6). The class tables are the MODIS and VIIRS sentinel values that share a byte with the NDSI percentage. Thresholding that byte is left to the user, in the open, because the choice of threshold and of what to do with cloud, night and water is the analysis:
ndsi_da = snow_ds["CGF_NDSI_Snow_Cover"]
valid_da = ndsi_da <= 100 # 200+ are the sentinels below
binary_da = (ndsi_da >= 40).where(valid_da) # NaN where the pixel says nothing
Put the upstream 256/265 fill values back to 255 and return uint8. |
|
Group a SNODAS day's tar into |
|
Build a georeferenced DataArray from one SNODAS |
easysnowdata.processing.wateryear#
Vectorized water-year helpers.
This is the single copy of these functions (§12 Q16): the scalar
utils.datetime_to_WY/datetime_to_DOWY that used to be applied with
pd.Index.map and the global_snow_networks utils/utils.py
implementations are both reconciled here. The names wy_start/wy_end
from that repo are water_year_bounds(), wy_date_range is
water_year_range(), wy_length is water_year_length() and
add_wy_coords is add_water_year_coords(); all of them also take a
hemisphere, which the originals did not.
A northern-hemisphere water year starts 1 October and is named for the
calendar year in which it ends (WY 2021 = 2020-10-01 … 2021-09-30); a
southern-hemisphere one starts 1 April and is named for the year it starts.
For aggregation prefer the pandas anchored offsets:
swe_ds.resample(time="YS-OCT").max(). Day-of-water-year has no pandas
primitive, hence day_of_water_year() and add_water_year_coords().
The two namings line up: water year N is, in both hemispheres, the snow season that peaks in calendar year N. The southern convention is the one the Andes SWE reanalysis uses (Cortés and Margulis 2017; its Dryad record, doi:10.5061/dryad.ngf1vhj0s: “WY1985 corresponds to April 1st, 1985 through March 31st, 1986”).
A (station, time) Dataset can mix hemispheres. hemisphere="auto"
in add_water_year_coords() gives each station its own water year from
its latitude (station_hemispheres()).
First and last day of water year year. |
|
Daily |
|
Number of days in water year year — 366 when 29 February falls inside it. |