Quickstart#
This library provides functions to hone in on coincident datasets and visualize their coverage.
The current recommended workflow is to:
start with a lidar dataset
reduce to region with coincident vantor stereo within an acceptable temporal range
optionally reduce further with additional datasets such as icesat-2 and gedi altimetry
This notebook provides an example starting from USGS 3DEP LiDAR in Colorado, USA
import geopandas as gpd
import coincident
print(coincident.__version__)
/home/docs/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/src/coincident/io/download.py:25: TqdmExperimentalWarning: Using `tqdm.autonotebook.tqdm` in notebook mode. Use `tqdm.tqdm` instead to force console mode (e.g. in jupyter console)
from tqdm.autonotebook import tqdm
0.6
Define an area of interest#
Searches often start with an spatial subset, in this notebook we know we are interested in datasets in Colorado. We recommend restricting searches to areas at the ‘State’ scale rather than Country or Global scales
aoi = gpd.read_file(
"https://raw.githubusercontent.com/unitedstates/districts/refs/heads/gh-pages/states/CO/shape.geojson"
)
aoi.explore(color="black", style_kwds=dict(fill=False))
Note
this polygon for the state of Colorado is simple and has only 4 vertices in the corners, but it’s good practice good to check the number of vertices you have before searching. The simpler your search polygons are the faster your searches will be!
print("Original number of vertices:", aoi.count_coordinates().iloc[0])
aoi = aoi.simplify(0.01)
print("Simplified number of vertices:", aoi.count_coordinates().iloc[0])
Original number of vertices: 260
Simplified number of vertices: 7
Uniform search method#
the coincident package provides a search() method that has the same syntax regardless of which dataset you are searching. Behind the scenes, polygons intersecting your area of interest are efficiently located and returned as a geodataframe.
For 3DEP LiDAR, we start by searching bounding boxes for each ‘workunit’. Once we identify a workunit, we load a precise polygon delineating the extent of LiDAR measurements.
The search function is essentially a pystac_client.ItemSearch with an extra argument dataset.
coincident.datasets.aliases
['vantor',
'3dep',
'3dep-1m',
'icesat-2',
'gedi',
'gliht',
'ablvis2_1',
'aflvis2_1',
'cop30',
'worldcover',
'tdx',
'noaa',
'ncalm',
'neon']
gf = coincident.search.search(
dataset="3dep",
intersects=aoi,
datetime=["2018", "2024"],
)
gf.explore(column="workunit", popup=True)
# From this search we identify a specific lidar acquisition of interest
gf = gf[gf.workunit == "CO_WestCentral_2019"]
gf
| minx | maxx | miny | maxy | geometry | workunit | workunit_id | project | project_id | start_datetime | ... | onemeter_reason | seamless_category | seamless_reason | lpc_link | sourcedem_link | metadata_link | collection | datetime | dayofyear | duration | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| fid | |||||||||||||||||||||
| 2456 | -107.989311 | -106.135696 | 38.134274 | 39.389549 | POLYGON ((-106.1357 38.13427, -106.1357 39.389... | CO_WestCentral_2019 | 175984 | CO_WestCentral_2019_A19 | 175987 | 2019-08-21 | ... | Meets 3DEP 1-m DEM requirements | Meets | Meets 3DEP seamless DEM requirements | https://rockyweb.usgs.gov/vdelivery/Datasets/S... | https://prd-tnm.s3.amazonaws.com/index.html?pr... | https://prd-tnm.s3.amazonaws.com/index.html?pr... | 3DEP | 2019-09-04 12:00:00 | 247 | 29 |
1 rows × 37 columns
Each Workunit has a unique ‘Feature ID’, or ‘FID’ that can be used to efficiently retrieve the full-resultion detailed MultiPolygon footprint from the USGS’s WESM.gpkg file in AWS
gf.index.values
array([2456])
USGS 3DEP Lidar#
Some datasets have additional functions available to load auxiliary data. For example, we can load original high resolution polygon. In addition to loading “swath” polygons to understand exact days when acquisitions were made.
Warning
Swath polygons are not available for all LiDAR workunits. They are more likely to exist for acquisitions after 2018.
gf_wesm = coincident.search.wesm.load_by_fid(fids=gf.index.values)
gf_wesm.explore(column="workunit", popup=True)
gf_wesm[["workunit", "start_datetime", "end_datetime", "duration"]] # duration in days
| workunit | start_datetime | end_datetime | duration | |
|---|---|---|---|---|
| 0 | CO_WestCentral_2019 | 2019-08-21 | 2019-09-19 | 29 |
Within this ‘workunit’ data was collected over 29 days, in order to see when data was collected within this polygon we need to load a corresponding ‘swath’ polygon. coincident provides a helper function for this, which loads the swath polygon for a given workunit
Note
Swath polygons have detailed timing information for individual LiDAR flight lines composing a given ‘workunit’. Not all workunits have swath polygons. They tend to be available for data collected after 2019.
# NOTE: be patient here, reading shapefiles from s3 can be slow depending on your bandwidth
gf_swath = coincident.search.wesm.get_swath_polygons(gf.workunit.iloc[0])
print("Number of swath polygons:", len(gf_swath))
Number of swath polygons: 314
# Plot them, simplify first for faster plotting
gf_swath["geometry"] = gf_swath.simplify(0.01)
gf_swath.explore(column="dayofyear", cmap="plasma")
As you can see in the above figure, the actual day of observation for any point on the ground can vary in complex ways based on the flight paths taken during the LiDAR collection period.
Vantor stereo search#
Now that we understand our lidar collection, let’s return to the uniform search function, and now search for Vantor stereo pairs. We are only interested in pairs in the same date range of the lidar. Note that searching by a simplified polygon (in this case back to the convex hull we started with) is recommended as most APIs have limits on the number of polygon vertices.
# Simplify search polygon and add date range
pad = gpd.pd.Timedelta(days=14)
date_range = [gf.start_datetime.iloc[0] - pad, gf.end_datetime.iloc[0] + pad]
aoi = gf.geometry
gf_vantor = coincident.search.search(
dataset="vantor",
intersects=aoi,
datetime=date_range,
filter="eo:cloud_cover < 20",
)
print(len(gf_vantor))
gf_vantor.head()
16
| type | stac_version | stac_extensions | id | target_to_spacecraft_elevation_start | target_to_spacecraft_elevation_min | links | assets | collection | datetime | ... | target_to_spacecraft_azimuth_max | target_to_spacecraft_azimuth_min | spacecraft_to_target_azimuth_start | target_to_spacecraft_azimuth_start | target_to_spacecraft_elevation_avg | target_to_spacecraft_elevation_end | target_to_spacecraft_elevation_max | bbox | geometry | dayofyear | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Feature | 1.1.0 | [https://stac-extensions.github.io/eo/v1.1.0/s... | 102001008EC5AC00 | 55.552526 | 55.552526 | [{'href': 'https://api.maxar.com/discovery/v1/... | {'browse': {'href': 'https://api.maxar.com/dis... | wv01 | 2019-09-30 20:55:50.706000+00:00 | ... | 166.21698 | 164.75037 | 346.21698 | 166.21698 | 58.692128 | 61.748469 | 61.748469 | {'xmin': -106.641662, 'ymin': 37.974755, 'xmax... | POLYGON ((-106.64098 39.03543, -106.64166 38.9... | 273 |
| 1 | Feature | 1.1.0 | [https://stac-extensions.github.io/eo/v1.1.0/s... | 102001008BD60800 | 67.098168 | 67.098168 | [{'href': 'https://api.maxar.com/discovery/v1/... | {'browse': {'href': 'https://api.maxar.com/dis... | wv01 | 2019-09-30 20:55:29.358000+00:00 | ... | 141.81210 | 132.28598 | 321.81210 | 141.81210 | 69.943833 | 72.345981 | 72.345981 | {'xmin': -106.792525, 'ymin': 37.981852, 'xmax... | POLYGON ((-106.79232 39.02773, -106.79092 38.9... | 273 |
| 2 | Feature | 1.1.0 | [https://stac-extensions.github.io/eo/v1.1.0/s... | 102001008BE9BB00 | 70.519505 | 68.761869 | [{'href': 'https://api.maxar.com/discovery/v1/... | {'browse': {'href': 'https://api.maxar.com/dis... | wv01 | 2019-09-30 20:54:59.407000+00:00 | ... | 69.03787 | 51.66277 | 249.03787 | 69.03787 | 69.721476 | 68.761869 | 70.519505 | {'xmin': -106.634773, 'ymin': 37.982046, 'xmax... | POLYGON ((-106.63038 39.01358, -106.63372 38.9... | 273 |
| 3 | Feature | 1.1.0 | [https://stac-extensions.github.io/eo/v1.1.0/s... | 1020010088168700 | 58.486978 | 54.500675 | [{'href': 'https://api.maxar.com/discovery/v1/... | {'browse': {'href': 'https://api.maxar.com/dis... | wv01 | 2019-09-30 20:54:39.207000+00:00 | ... | 45.07959 | 35.63832 | 225.07959 | 45.07959 | 56.454620 | 54.500675 | 58.486978 | {'xmin': -106.812163, 'ymin': 37.955262, 'xmax... | POLYGON ((-106.80822 39.01663, -106.80613 38.9... | 273 |
| 5 | Feature | 1.1.0 | [https://stac-extensions.github.io/eo/v1.1.0/s... | 104001005232D700 | 76.497944 | 76.497944 | [{'href': 'https://api.maxar.com/discovery/v1/... | {'browse': {'href': 'https://api.maxar.com/dis... | wv03-vnir | 2019-09-23 18:16:05.936000+00:00 | ... | 105.60605 | 103.75317 | 283.75317 | 103.75317 | 76.888955 | 77.173199 | 77.173199 | {'xmin': -108.075387, 'ymin': 38.875481, 'xmax... | POLYGON ((-108.07422 39.13516, -108.07281 39.1... | 266 |
5 rows × 69 columns
with gpd.pd.option_context("display.max_rows", None, "display.max_columns", None):
print(gf_vantor.iloc[0])
type Feature
stac_version 1.1.0
stac_extensions [https://stac-extensions.github.io/eo/v1.1.0/s...
id 102001008EC5AC00
target_to_spacecraft_elevation_start 55.552526
target_to_spacecraft_elevation_min 55.552526
links [{'href': 'https://api.maxar.com/discovery/v1/...
assets {'browse': {'href': 'https://api.maxar.com/dis...
collection wv01
datetime 2019-09-30 20:55:50.706000+00:00
title Maxar WV01 Image 102001008EC5AC00
gsd 0.627205
eo:bands [{'name': 'pan', 'center_wavelength': 650.0}]
platform worldview-01
utc_hour 20
local_hour 13
instruments [VNIR]
associations []
view:azimuth 345.480086
constellation maxar
off_nadir_avg 28.822183
off_nadir_end 26.05217
off_nadir_max 31.65722
off_nadir_min 26.05217
utc_month_day 09-30
eo:cloud_cover 0.003715
scan_direction forward
view:off_nadir 28.822183
local_month_day 09-30
off_nadir_start 31.65722
timezone_offset -7
utc_time_of_day 20:55:50
collect_time_end 2019-09-30T20:55:58.040916Z
view:sun_azimuth 220.865526
local_time_of_day 13:55:50
collect_time_start 2019-09-30T20:55:50.706955Z
pan_resolution_avg 0.627205
pan_resolution_end 0.598924
pan_resolution_max 0.658881
pan_resolution_min 0.598924
view:sun_elevation 40.322631
multi_resolution_avg NaN
multi_resolution_end NaN
multi_resolution_max NaN
multi_resolution_min NaN
pan_resolution_start 0.658881
acquisition_rev_number 66956
multi_resolution_start NaN
view:sun_elevation_max 40.70389
view:sun_elevation_min 39.92902
geolocation_uncertainty 8.339009
stereo_pair_identifiers [11a11283-6661-4f91-9bcb-5dab3c6a5d02-inv]
local_time_of_day_with_timezone 13:55:50-0700
spacecraft_to_target_azimuth_avg 345.480086
spacecraft_to_target_azimuth_end 344.75037
spacecraft_to_target_azimuth_max 346.21698
spacecraft_to_target_azimuth_min 344.75037
target_to_spacecraft_azimuth_avg 165.480086
target_to_spacecraft_azimuth_end 164.75037
target_to_spacecraft_azimuth_max 166.21698
target_to_spacecraft_azimuth_min 164.75037
spacecraft_to_target_azimuth_start 346.21698
target_to_spacecraft_azimuth_start 166.21698
target_to_spacecraft_elevation_avg 58.692128
target_to_spacecraft_elevation_end 61.748469
target_to_spacecraft_elevation_max 61.748469
bbox {'xmin': -106.641662, 'ymin': 37.974755, 'xmax...
geometry POLYGON ((-106.640977 39.035433, -106.641662 3...
dayofyear 273
Name: 0, dtype: object
# Group by stereo pair id (NOTE: assumes single id per mono acquisition)
gf_vantor["stereo_pair_id"] = gf_vantor.stereo_pair_identifiers.str[0]
gf_stereo = gf_vantor.dissolve(by="stereo_pair_id", as_index=False)
gf_stereo.explore(column="stereo_pair_identifiers")
m = gf_wesm.explore(color="black")
gf_vantor.explore(column="dayofyear", cmap="plasma", m=m, popup=True)
Restrict polygon footprints to area of overlap#
We will make regular use of the GeoPandas.overlay() function to shrink original acquisition footprints so that they only overlap an area of interest.
# Consider area of overlap
m = gf_wesm.explore(color="black")
# NOTE: don't really need wesm attributes in result, so just use geodataframe w/ geometry column
# gf_i = gf_wesm[['geometry']].overlay(gf_stereo, how='intersection')
gf_i = gf_stereo.overlay(gf_wesm[["geometry"]], how="intersection")
gf_i.explore(column="dayofyear", cmap="plasma", m=m)
Exclude very small overlaps#
Consider further filtering results by a minimum overlap area criteria, or by a more precise estimate of the number of days elapsed for the vantor stereo acquisition relative to lidar estimated from swath polygons
min_area_km2 = 20
gf_i = coincident.overlaps.subset_by_minimum_area(gf_i, min_area_km2)
# Look at intersection with swath polygons for exact date difference
# Since both have 'dayofyear', these cols get expanded to dayofyear_1 and dayofyear_2
stereo_pair = gf_i.iloc[[0]]
gf_dt = gf_swath.overlay(stereo_pair, how="intersection")
print("Vantor Stereo Acquisition DOY - Swath Lidar DOY:")
print(stereo_pair.stereo_pair_id.values[0])
(gf_dt.dayofyear_2 - gf_dt.dayofyear_1).describe()
Vantor Stereo Acquisition DOY - Swath Lidar DOY:
11a11283-6661-4f91-9bcb-5dab3c6a5d02-inv
count 84.000000
mean 30.702381
std 2.736543
min 27.000000
25% 28.750000
50% 30.000000
75% 34.000000
max 36.000000
dtype: float64
GEDI#
Search for Coincident GEDI L2A.
# We've refined our search polygon again, so use a new AOI
aoi = gpd.GeoSeries(gf_i.union_all().convex_hull, crs="EPSG:4326")
gf_gedi = coincident.search.search(
dataset="gedi",
intersects=aoi,
datetime=date_range,
)
print(len(gf_gedi))
gf_gedi.head()
9
| type | stac_version | stac_extensions | id | storage:schemes | end_datetime | links | assets | collection | datetime | start_datetime | bbox | geometry | dayofyear | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Feature | 1.1.0 | [https://stac-extensions.github.io/storage/v2.... | GEDI02_A_2019221181154_O03725_02_T03077_02_003... | {'aws': {'type': 'aws-s3', 'platform': 'https:... | 2019-08-09 19:44:41+00:00 | [{'href': 'https://cmr.earthdata.nasa.gov/stac... | {'browse': {'href': 'https://data.lpdaac.earth... | GEDI02_A_002 | 2019-08-09 18:11:54+00:00 | 2019-08-09 18:11:54+00:00 | {'xmin': -130.7824147, 'ymin': 14.5375478, 'xm... | POLYGON ((-130.74077 14.53755, -128.39101 17.5... | 221 |
| 1 | Feature | 1.1.0 | [https://stac-extensions.github.io/storage/v2.... | GEDI02_A_2019228215125_O03836_03_T03938_02_003... | {'aws': {'type': 'aws-s3', 'platform': 'https:... | 2019-08-16 23:24:15+00:00 | [{'href': 'https://cmr.earthdata.nasa.gov/stac... | {'browse': {'href': 'https://data.lpdaac.earth... | GEDI02_A_002 | 2019-08-16 21:51:25+00:00 | 2019-08-16 21:51:25+00:00 | {'xmin': -154.5108058, 'ymin': 0.0764856, 'xma... | POLYGON ((-154.51081 51.7766, -148.53387 51.60... | 228 |
| 2 | Feature | 1.1.0 | [https://stac-extensions.github.io/storage/v2.... | GEDI02_A_2019232140204_O03893_02_T00583_02_003... | {'aws': {'type': 'aws-s3', 'platform': 'https:... | 2019-08-20 15:34:53+00:00 | [{'href': 'https://cmr.earthdata.nasa.gov/stac... | {'browse': {'href': 'https://data.lpdaac.earth... | GEDI02_A_002 | 2019-08-20 14:02:04+00:00 | 2019-08-20 14:02:04+00:00 | {'xmin': -131.649277, 'ymin': 15.7666963, 'xma... | POLYGON ((-131.60759 15.7667, -129.21939 18.73... | 232 |
| 3 | Feature | 1.1.0 | [https://stac-extensions.github.io/storage/v2.... | GEDI02_A_2019247142840_O04126_03_T02362_02_003... | {'aws': {'type': 'aws-s3', 'platform': 'https:... | 2019-09-04 16:01:29+00:00 | [{'href': 'https://cmr.earthdata.nasa.gov/stac... | {'browse': {'href': 'https://data.lpdaac.earth... | GEDI02_A_002 | 2019-09-04 14:28:40+00:00 | 2019-09-04 14:28:40+00:00 | {'xmin': -154.8002265, 'ymin': -0.0163617, 'xm... | POLYGON ((-154.80023 51.77634, -148.81565 51.5... | 247 |
| 4 | Feature | 1.1.0 | [https://stac-extensions.github.io/storage/v2.... | GEDI02_A_2019251125026_O04187_03_T01704_02_003... | {'aws': {'type': 'aws-s3', 'platform': 'https:... | 2019-09-08 14:23:15+00:00 | [{'href': 'https://cmr.earthdata.nasa.gov/stac... | {'browse': {'href': 'https://data.lpdaac.earth... | GEDI02_A_002 | 2019-09-08 12:50:26+00:00 | 2019-09-08 12:50:26+00:00 | {'xmin': -153.6270991, 'ymin': 0.0029707, 'xma... | POLYGON ((-153.6271 51.77622, -147.64024 51.60... | 251 |
gf_gedi.explore()
Altimeter granules span a large geographic area! So let’s again cut results down to the area of intersection.
# Normalize colormap across both dataframes
vmin, vmax = gpd.pd.concat([gf_i.dayofyear, gf_gedi.dayofyear]).agg(["min", "max"])
cmap = "plasma"
# NOTE: here we just take the boundary of the union of all vantor+lidar regions to avoid many overlay geometries
union = gpd.GeoDataFrame(geometry=[gf_i.union_all()], crs="EPSG:4326")
m = gf_i.explore(column="dayofyear", cmap=cmap, vmin=vmin, vmax=vmax)
gf_gedi = union.overlay(gf_gedi, how="intersection")
gf_gedi.explore(m=m, column="dayofyear", cmap=cmap, vmin=vmin, vmax=vmax, legend=False)
m
Immediately we see some potential for close-in time acquisitions.
Note
As an additional step, you might want to add another filtering step to remove tiny polygons
# For each Stereo pair, describe number of altimeter passes and day offset
stereo_pair = gf_i.iloc[[0]]
gf_dt = stereo_pair.overlay(gf_gedi, how="intersection")
m = stereo_pair.explore(
column="dayofyear", vmin=vmin, vmax=vmax, cmap=cmap, legend=False
)
gf_dt.explore(m=m, column="dayofyear_2", vmin=vmin, vmax=vmax, cmap=cmap)
print("Vantor Stereo Acquisition DOY - GEDI DOY:")
print(stereo_pair.stereo_pair_identifiers.values[0])
(gf_dt.dayofyear_1 - gf_dt.dayofyear_2).describe()
Vantor Stereo Acquisition DOY - GEDI DOY:
['11a11283-6661-4f91-9bcb-5dab3c6a5d02-inv']
count 4.000000
mean 23.000000
std 17.492856
min 3.000000
25% 14.250000
50% 22.000000
75% 30.750000
max 45.000000
dtype: float64
# Consider just the minimum offset for each stereo pair
# NOTE: probably a fancier way to do this using a multiindex/groupby
print("Vantor Stereo Acquisition DOY - GEDI DOY")
for i in range(len(gf_i)):
stereo_pair = gf_i.iloc[[i]]
gf_dt = stereo_pair.overlay(gf_gedi, how="intersection")
min_dt = (gf_dt.dayofyear_1 - gf_dt.dayofyear_2).min()
print(stereo_pair.stereo_pair_identifiers.values[0], min_dt)
Vantor Stereo Acquisition DOY - GEDI DOY
['11a11283-6661-4f91-9bcb-5dab3c6a5d02-inv'] 3
['21d1d4b2-47a4-4c4d-a26f-5c688199b518-inv'] -36
['28b60c45-3164-4cd0-9b43-67dac4e8c7b4-inv'] -31
['41fef87b-0d7c-46c2-a598-bcd178dfb9e0-inv'] -29
['6002024a-ca85-4f3c-8c7f-ce849c175a52-inv'] 3
Note
In this case, we have some altimeter acquisitions that were just 3 days after a vantor stereo acquisition!
ICESat-2#
Search for Coincident ICESat-2 Altimetry (ATL03)
gf_is2 = coincident.search.search(
dataset="icesat-2",
intersects=aoi,
datetime=date_range,
)
print(len(gf_is2))
gf_is2
5
| type | stac_version | stac_extensions | id | storage:schemes | end_datetime | links | assets | collection | datetime | start_datetime | bbox | geometry | dayofyear | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Feature | 1.1.0 | [https://stac-extensions.github.io/storage/v2.... | ATL03_20190807082015_06150402_007_01.h5 | {'aws': {'type': 'aws-s3', 'platform': 'https:... | 2019-08-07 08:28:45.648000+00:00 | [{'href': 'https://cmr.earthdata.nasa.gov/clou... | {'browse': {'href': 'https://data.nsidc.earthd... | ATL03_007 | 2019-08-07 08:20:14.997000+00:00 | 2019-08-07 08:20:14.997000+00:00 | {'xmin': -109.90888, 'ymin': 26.94277, 'xmax':... | POLYGON ((-109.68811 59.53895, -109.90888 59.5... | 219 |
| 1 | Feature | 1.1.0 | [https://stac-extensions.github.io/storage/v2.... | ATL03_20190903191210_10340406_007_01.h5 | {'aws': {'type': 'aws-s3', 'platform': 'https:... | 2019-09-03 19:20:40.344000+00:00 | [{'href': 'https://cmr.earthdata.nasa.gov/clou... | {'browse': {'href': 'https://data.nsidc.earthd... | ATL03_007 | 2019-09-03 19:12:09.750000+00:00 | 2019-09-03 19:12:09.750000+00:00 | {'xmin': -108.09761, 'ymin': 26.96541, 'xmax':... | POLYGON ((-103.41614 59.54102, -103.63703 59.5... | 246 |
| 2 | Feature | 1.1.0 | [https://stac-extensions.github.io/storage/v2.... | ATL03_20190905065620_10570402_007_01.h5 | {'aws': {'type': 'aws-s3', 'platform': 'https:... | 2019-09-05 07:04:49.503000+00:00 | [{'href': 'https://cmr.earthdata.nasa.gov/clou... | {'browse': {'href': 'https://data.nsidc.earthd... | ATL03_007 | 2019-09-05 06:56:19.242000+00:00 | 2019-09-05 06:56:19.242000+00:00 | {'xmin': -109.64979, 'ymin': 26.94272, 'xmax':... | POLYGON ((-109.42924 59.53775, -109.64979 59.5... | 248 |
| 3 | Feature | 1.1.0 | [https://stac-extensions.github.io/storage/v2.... | ATL03_20190909064801_11180402_007_01.h5 | {'aws': {'type': 'aws-s3', 'platform': 'https:... | 2019-09-09 06:56:31.581000+00:00 | [{'href': 'https://cmr.earthdata.nasa.gov/clou... | {'browse': {'href': 'https://data.nsidc.earthd... | ATL03_007 | 2019-09-09 06:48:00.991000+00:00 | 2019-09-09 06:48:00.991000+00:00 | {'xmin': -110.29973, 'ymin': 26.95221, 'xmax':... | POLYGON ((-110.07843 59.54576, -110.29973 59.5... | 252 |
| 4 | Feature | 1.1.0 | [https://stac-extensions.github.io/storage/v2.... | ATL03_20191002174817_00890506_007_01.h5 | {'aws': {'type': 'aws-s3', 'platform': 'https:... | 2019-10-02 17:56:47.166000+00:00 | [{'href': 'https://cmr.earthdata.nasa.gov/clou... | {'browse': {'href': 'https://data.nsidc.earthd... | ATL03_007 | 2019-10-02 17:48:16.589000+00:00 | 2019-10-02 17:48:16.589000+00:00 | {'xmin': -107.83933, 'ymin': 26.96541, 'xmax':... | POLYGON ((-103.15541 59.54093, -103.37636 59.5... | 275 |
# Normalize colormap across both dataframes
vmin, vmax = gpd.pd.concat([gf_i.dayofyear, gf_is2.dayofyear]).agg(["min", "max"])
cmap = "plasma"
# NOTE: here we just take the boundary of the union of all vantor+lidar regions to avoid many overlay geometries
union = gpd.GeoDataFrame(geometry=[gf_i.union_all()], crs="EPSG:4326")
m = gf_i.explore(
column="dayofyear", cmap=cmap, vmin=vmin, vmax=vmax, style_kwds=dict(color="black")
)
gf_is2 = union.overlay(gf_is2, how="intersection")
gf_is2.explore(m=m, column="dayofyear", cmap=cmap, vmin=vmin, vmax=vmax, legend=False)
m
# For each Stereo pair, describe number of altimeter passes and day offset
stereo_pair = gf_i.iloc[[0]]
gf_dt = stereo_pair.overlay(gf_is2, how="intersection")
m = stereo_pair.explore(
column="dayofyear",
vmin=vmin,
vmax=vmax,
cmap=cmap,
legend=False,
style_kwds=dict(color="black"),
)
gf_dt.explore(m=m, column="dayofyear_2", vmin=vmin, vmax=vmax, cmap=cmap)
print("Vantor Stereo Acquisition DOY - ICESat-2 DOY")
for i in range(len(gf_i)):
stereo_pair = gf_i.iloc[[i]]
gf_dt = stereo_pair.overlay(gf_is2, how="intersection")
min_dt = (gf_dt.dayofyear_1 - gf_dt.dayofyear_2).min()
print(stereo_pair.stereo_pair_identifiers.values[0], min_dt)
Vantor Stereo Acquisition DOY - ICESat-2 DOY
['11a11283-6661-4f91-9bcb-5dab3c6a5d02-inv'] -2
['21d1d4b2-47a4-4c4d-a26f-5c688199b518-inv'] -18
['28b60c45-3164-4cd0-9b43-67dac4e8c7b4-inv'] -36
['41fef87b-0d7c-46c2-a598-bcd178dfb9e0-inv'] -30
['6002024a-ca85-4f3c-8c7f-ce849c175a52-inv'] 27
Summary#
Any of these Vantor stereo pairs could be worth ordering. But if the goal is to have coincident acquisitions as close as possible in time ‘11a11283-6661-4f91-9bcb-5dab3c6a5d02-inv’ seems like a good bet:
There is an overlapping ICESat-2 track 2 days later
There is an overlapping GEDI track 3 days earlier
3DEP Lidar was acquired 27 to 36 days earlier
# Can save this dataframe, or just note STAC ids to work with later ('102001008EC5AC00','102001008BE9BB00')
pair_id = "11a11283-6661-4f91-9bcb-5dab3c6a5d02-inv"
full_frame = gf_vantor[gf_vantor.stereo_pair_id == pair_id]
subset = gf_i[gf_i.stereo_pair_id == pair_id]
# Save the original STAC metadata and polygon
full_frame.to_parquet("/tmp/vantor-mono-images.parquet")
# Also save the subsetted region for this stereo pair
subset.to_parquet("/tmp/stereo-subset.parquet")