Vantor Stereo Data#
This notebooks illustrates basic search and visualization of Vantor Stereo Imagery.
Coincident uses the Vantor ‘discovery’ STAC API to find high resolution stereo data.
import coincident
import geopandas as gpd
import matplotlib.pyplot as plt
%matplotlib inline
/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
Basic search#
Below we use a simple bounding box, date range, and filter to find scenes with cloud cover less than 25%
bbox = [-71.8, 42.0, -71.5, 42.1]
gf_stereo = coincident.search.search(
dataset="vantor",
bbox=bbox,
datetime=["2021-03-08", "2021-04-01"],
filter="eo:cloud_cover < 25",
)
len(gf_stereo)
2
# Print all the metadata for a particular scene
# Note 'stereo_pair_identifiers' and 'id' for ordering full-resolution data
with gpd.pd.option_context("display.max_rows", None, "display.max_columns", None):
print(gf_stereo.iloc[0])
type Feature
stac_version 1.1.0
stac_extensions [https://stac-extensions.github.io/eo/v1.1.0/s...
id 10200100A7501F00
target_to_spacecraft_elevation_start 52.562387
target_to_spacecraft_elevation_min 52.562387
links [{'href': 'https://api.maxar.com/discovery/v1/...
assets {'browse': {'href': 'https://api.maxar.com/dis...
collection wv01
datetime 2021-03-22 18:32:25.619000+00:00
title Maxar WV01 Image 10200100A7501F00
gsd 0.668156
eo:bands [{'name': 'pan', 'center_wavelength': 650.0}]
platform worldview-01
utc_hour 18
local_hour 13
instruments [VNIR]
associations []
view:azimuth 324.83049
constellation maxar
off_nadir_avg 31.826101
off_nadir_end 29.628012
off_nadir_max 34.335285
off_nadir_min 29.628012
utc_month_day 03-22
eo:cloud_cover 0.030452
scan_direction forward
view:off_nadir 31.826101
local_month_day 03-22
off_nadir_start 34.335285
timezone_offset -5
utc_time_of_day 18:32:25
collect_time_end 2021-03-22T18:32:32.966515Z
view:sun_azimuth 214.982759
local_time_of_day 13:32:25
collect_time_start 2021-03-22T18:32:25.619225Z
pan_resolution_avg 0.668156
pan_resolution_end 0.639698
pan_resolution_max 0.703351
pan_resolution_min 0.639698
view:sun_elevation 43.057353
multi_resolution_avg NaN
multi_resolution_end NaN
multi_resolution_max NaN
multi_resolution_min NaN
pan_resolution_start 0.703351
acquisition_rev_number 75166
multi_resolution_start NaN
view:sun_elevation_max 43.46007
view:sun_elevation_min 42.596928
geolocation_uncertainty 7.867035
stereo_pair_identifiers [1cc34eb7-0319-4b27-ba56-3dcbf419a6fe-inv]
local_time_of_day_with_timezone 13:32:25-0500
spacecraft_to_target_azimuth_avg 324.83049
spacecraft_to_target_azimuth_end 321.96863
spacecraft_to_target_azimuth_max 327.8639
spacecraft_to_target_azimuth_min 321.96863
target_to_spacecraft_azimuth_avg 144.830487
target_to_spacecraft_azimuth_end 141.968628
target_to_spacecraft_azimuth_max 147.863892
target_to_spacecraft_azimuth_min 141.968628
spacecraft_to_target_azimuth_start 327.8639
target_to_spacecraft_azimuth_start 147.863892
target_to_spacecraft_elevation_avg 55.361341
target_to_spacecraft_elevation_end 57.804375
target_to_spacecraft_elevation_max 57.804375
bbox {'xmin': -71.814808, 'ymin': 41.953262, 'xmax'...
geometry POLYGON ((-71.814808 43.062211, -71.813892 42....
dayofyear 81
Name: 1, dtype: object
popcols = ["title", "eo:cloud_cover", "datetime", "view:azimuth", "view:off_nadir"]
gf_stereo.explore(popup=popcols)
Load browse image#
Vantor provides monochrome uint8 or RGB browse images at ~30m resolution that we can visualize before ordering the full-resolution scenes.
# We first convert from geopandas metadata to a pystac items
items = coincident.search.to_pystac_items(gf_stereo)
da = coincident.io.xarray.open_vantor_browse(items[0]).squeeze()
da
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/file_manager.py:219, in CachingFileManager._acquire_with_cache_info(self, needs_lock)
218 try:
--> 219 file = self._cache[self._key]
220 except KeyError:
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/lru_cache.py:56, in LRUCache.__getitem__(self, key)
55 with self._lock:
---> 56 value = self._cache[key]
57 self._cache.move_to_end(key)
KeyError: [<function open at 0x7f8457e483b0>, ('https://api.maxar.com/discovery/v1/catalogs/imagery/collections/wv01/items/10200100A7501F00/assets/collections/dg-archive/assets/browse/10200100A7501F00.browse.tif',), 'r', (('overview_level', 0), ('sharing', False)), '32195ac6-02fe-4091-97ab-5730290add1a']
During handling of the above exception, another exception occurred:
CPLE_HttpResponseError Traceback (most recent call last)
File rasterio/_base.pyx:320, in rasterio._base.DatasetBase.__init__()
--> 320 'Could not get source, probably due dynamically evaluated source code.'
File rasterio/_base.pyx:232, in rasterio._base.open_dataset()
--> 232 'Could not get source, probably due dynamically evaluated source code.'
File rasterio/_err.pyx:359, in rasterio._err.exc_wrap_pointer()
--> 359 'Could not get source, probably due dynamically evaluated source code.'
CPLE_HttpResponseError: HTTP response code: 401
During handling of the above exception, another exception occurred:
RasterioIOError Traceback (most recent call last)
Cell In[6], line 1
----> 1 da = coincident.io.xarray.open_vantor_browse(items[0]).squeeze()
2 da
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/src/coincident/io/xarray.py:180, in open_vantor_browse(item, overview_level)
175 env = rasterio.Env(
176 GDAL_DISABLE_READDIR_ON_OPEN="EMPTY_DIR",
177 GDAL_HTTP_HEADERS=f"VANTOR-API-KEY:{os.environ['VANTOR_API_KEY']}",
178 )
179 with env:
--> 180 return xr.open_dataarray(
181 href,
182 engine="rasterio",
183 mask_and_scale=False, # otherwise uint8 -> float32!
184 backend_kwargs={"open_kwargs": {"overview_level": overview_level}},
185 )
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/api.py:814, in open_dataarray(filename_or_obj, engine, chunks, cache, decode_cf, mask_and_scale, decode_times, decode_timedelta, use_cftime, concat_characters, decode_coords, drop_variables, create_default_indexes, inline_array, chunked_array_type, from_array_kwargs, backend_kwargs, **kwargs)
631 def open_dataarray(
632 filename_or_obj: T_PathFileOrDataStore,
633 *,
(...) 652 **kwargs,
653 ) -> DataArray:
654 """Open a DataArray from a file or file-like object containing a single
655 data variable.
656
(...) 811 open_dataset
812 """
--> 814 dataset = open_dataset(
815 filename_or_obj,
816 decode_cf=decode_cf,
817 mask_and_scale=mask_and_scale,
818 decode_times=decode_times,
819 concat_characters=concat_characters,
820 decode_coords=decode_coords,
821 engine=engine,
822 chunks=chunks,
823 cache=cache,
824 drop_variables=drop_variables,
825 create_default_indexes=create_default_indexes,
826 inline_array=inline_array,
827 chunked_array_type=chunked_array_type,
828 from_array_kwargs=from_array_kwargs,
829 backend_kwargs=backend_kwargs,
830 use_cftime=use_cftime,
831 decode_timedelta=decode_timedelta,
832 **kwargs,
833 )
835 if len(dataset.data_vars) != 1:
836 if len(dataset.data_vars) == 0:
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/api.py:607, in open_dataset(filename_or_obj, engine, chunks, cache, decode_cf, mask_and_scale, decode_times, decode_timedelta, use_cftime, concat_characters, decode_coords, drop_variables, create_default_indexes, inline_array, chunked_array_type, from_array_kwargs, backend_kwargs, **kwargs)
595 decoders = _resolve_decoders_kwargs(
596 decode_cf,
597 open_backend_dataset_parameters=backend.open_dataset_parameters,
(...) 603 decode_coords=decode_coords,
604 )
606 overwrite_encoded_chunks = kwargs.pop("overwrite_encoded_chunks", None)
--> 607 backend_ds = backend.open_dataset(
608 filename_or_obj,
609 drop_variables=drop_variables,
610 **decoders,
611 **kwargs,
612 )
613 ds = _dataset_from_backend_dataset(
614 backend_ds,
615 filename_or_obj,
(...) 626 **kwargs,
627 )
628 return ds
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/rioxarray/xarray_plugin.py:59, in RasterioBackend.open_dataset(self, filename_or_obj, drop_variables, parse_coordinates, lock, masked, mask_and_scale, variable, group, default_name, decode_coords, decode_times, decode_timedelta, band_as_variable, open_kwargs)
57 if open_kwargs is None:
58 open_kwargs = {}
---> 59 rds = _io.open_rasterio(
60 filename_or_obj,
61 parse_coordinates=parse_coordinates,
62 cache=False,
63 lock=lock,
64 masked=masked,
65 mask_and_scale=mask_and_scale,
66 variable=variable,
67 group=group,
68 default_name=default_name,
69 decode_times=decode_times,
70 decode_timedelta=decode_timedelta,
71 band_as_variable=band_as_variable,
72 **open_kwargs,
73 )
74 if isinstance(rds, xarray.DataArray):
75 dataset = rds.to_dataset()
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/rioxarray/_io.py:1140, in open_rasterio(filename, parse_coordinates, chunks, cache, lock, masked, mask_and_scale, variable, group, default_name, decode_times, decode_timedelta, band_as_variable, **open_kwargs)
1138 else:
1139 manager = URIManager(file_opener, filename, mode="r", kwargs=open_kwargs)
-> 1140 riods = manager.acquire()
1141 captured_warnings = rio_warnings.copy()
1143 # raise the NotGeoreferencedWarning if applicable
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/file_manager.py:201, in CachingFileManager.acquire(self, needs_lock)
186 def acquire(self, needs_lock: bool = True) -> T_File:
187 """Acquire a file object from the manager.
188
189 A new file is only opened if it has expired from the
(...) 199 An open file object, as returned by ``opener(*args, **kwargs)``.
200 """
--> 201 file, _ = self._acquire_with_cache_info(needs_lock)
202 return file
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/file_manager.py:225, in CachingFileManager._acquire_with_cache_info(self, needs_lock)
223 kwargs = kwargs.copy()
224 kwargs["mode"] = self._mode
--> 225 file = self._opener(*self._args, **kwargs)
226 if self._mode == "w":
227 # ensure file doesn't get overridden when opened again
228 self._mode = "a"
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/rasterio/env.py:464, in ensure_env_with_credentials.<locals>.wrapper(*args, **kwds)
461 session = DummySession()
463 with env_ctor(session=session):
--> 464 return f(*args, **kwds)
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/rasterio/__init__.py:367, in open(fp, mode, driver, width, height, count, crs, transform, dtype, nodata, sharing, thread_safe, opener, **kwargs)
364 path = _parse_path(raw_dataset_path)
366 if mode == "r":
--> 367 dataset = DatasetReader(path, driver=driver, sharing=sharing, thread_safe=thread_safe, **kwargs)
368 elif mode == "r+":
369 dataset = get_writer_for_path(path, driver=driver)(
370 path, mode, driver=driver, sharing=sharing, **kwargs
371 )
File rasterio/_base.pyx:329, in rasterio._base.DatasetBase.__init__()
--> 329 'Could not get source, probably due dynamically evaluated source code.'
RasterioIOError: HTTP response code: 401
Plot browse image#
For convenience you can create simplified figures directly without first loading the data with Xarray. Browse images are cloud-optimized-geotiffs with overviews for efficient previewing.
coincident.plot.plot_vantor_browse(items[0], overview_level=2);
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/file_manager.py:219, in CachingFileManager._acquire_with_cache_info(self, needs_lock)
218 try:
--> 219 file = self._cache[self._key]
220 except KeyError:
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/lru_cache.py:56, in LRUCache.__getitem__(self, key)
55 with self._lock:
---> 56 value = self._cache[key]
57 self._cache.move_to_end(key)
KeyError: [<function open at 0x7f8457e483b0>, ('https://api.maxar.com/discovery/v1/catalogs/imagery/collections/wv01/items/10200100A7501F00/assets/collections/dg-archive/assets/browse/10200100A7501F00.browse.tif',), 'r', (('overview_level', 2), ('sharing', False)), '1bb4b5bb-fb7f-4d3f-a9d1-00f718675dc8']
During handling of the above exception, another exception occurred:
CPLE_HttpResponseError Traceback (most recent call last)
File rasterio/_base.pyx:320, in rasterio._base.DatasetBase.__init__()
--> 320 'Could not get source, probably due dynamically evaluated source code.'
File rasterio/_base.pyx:232, in rasterio._base.open_dataset()
--> 232 'Could not get source, probably due dynamically evaluated source code.'
File rasterio/_err.pyx:359, in rasterio._err.exc_wrap_pointer()
--> 359 'Could not get source, probably due dynamically evaluated source code.'
CPLE_HttpResponseError: HTTP response code: 401
During handling of the above exception, another exception occurred:
RasterioIOError Traceback (most recent call last)
Cell In[7], line 1
----> 1 coincident.plot.plot_vantor_browse(items[0], overview_level=2);
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/src/coincident/plot/matplotlib.py:139, in plot_vantor_browse(item, ax, overview_level)
122 def plot_vantor_browse(
123 item: pystac.Item, ax: plt.Axes | None = None, overview_level: int = 0
124 ) -> plt.Axes:
125 """
126 Map view of Vantor browse image from a STAC item using Matplotlib.
127
(...) 137 The Matplotlib Axes object with the plot.
138 """
--> 139 da = open_vantor_browse(item, overview_level=overview_level)
141 if ax is None:
142 _, ax = plt.subplots(figsize=(8, 11))
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/src/coincident/io/xarray.py:180, in open_vantor_browse(item, overview_level)
175 env = rasterio.Env(
176 GDAL_DISABLE_READDIR_ON_OPEN="EMPTY_DIR",
177 GDAL_HTTP_HEADERS=f"VANTOR-API-KEY:{os.environ['VANTOR_API_KEY']}",
178 )
179 with env:
--> 180 return xr.open_dataarray(
181 href,
182 engine="rasterio",
183 mask_and_scale=False, # otherwise uint8 -> float32!
184 backend_kwargs={"open_kwargs": {"overview_level": overview_level}},
185 )
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/api.py:814, in open_dataarray(filename_or_obj, engine, chunks, cache, decode_cf, mask_and_scale, decode_times, decode_timedelta, use_cftime, concat_characters, decode_coords, drop_variables, create_default_indexes, inline_array, chunked_array_type, from_array_kwargs, backend_kwargs, **kwargs)
631 def open_dataarray(
632 filename_or_obj: T_PathFileOrDataStore,
633 *,
(...) 652 **kwargs,
653 ) -> DataArray:
654 """Open a DataArray from a file or file-like object containing a single
655 data variable.
656
(...) 811 open_dataset
812 """
--> 814 dataset = open_dataset(
815 filename_or_obj,
816 decode_cf=decode_cf,
817 mask_and_scale=mask_and_scale,
818 decode_times=decode_times,
819 concat_characters=concat_characters,
820 decode_coords=decode_coords,
821 engine=engine,
822 chunks=chunks,
823 cache=cache,
824 drop_variables=drop_variables,
825 create_default_indexes=create_default_indexes,
826 inline_array=inline_array,
827 chunked_array_type=chunked_array_type,
828 from_array_kwargs=from_array_kwargs,
829 backend_kwargs=backend_kwargs,
830 use_cftime=use_cftime,
831 decode_timedelta=decode_timedelta,
832 **kwargs,
833 )
835 if len(dataset.data_vars) != 1:
836 if len(dataset.data_vars) == 0:
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/api.py:607, in open_dataset(filename_or_obj, engine, chunks, cache, decode_cf, mask_and_scale, decode_times, decode_timedelta, use_cftime, concat_characters, decode_coords, drop_variables, create_default_indexes, inline_array, chunked_array_type, from_array_kwargs, backend_kwargs, **kwargs)
595 decoders = _resolve_decoders_kwargs(
596 decode_cf,
597 open_backend_dataset_parameters=backend.open_dataset_parameters,
(...) 603 decode_coords=decode_coords,
604 )
606 overwrite_encoded_chunks = kwargs.pop("overwrite_encoded_chunks", None)
--> 607 backend_ds = backend.open_dataset(
608 filename_or_obj,
609 drop_variables=drop_variables,
610 **decoders,
611 **kwargs,
612 )
613 ds = _dataset_from_backend_dataset(
614 backend_ds,
615 filename_or_obj,
(...) 626 **kwargs,
627 )
628 return ds
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/rioxarray/xarray_plugin.py:59, in RasterioBackend.open_dataset(self, filename_or_obj, drop_variables, parse_coordinates, lock, masked, mask_and_scale, variable, group, default_name, decode_coords, decode_times, decode_timedelta, band_as_variable, open_kwargs)
57 if open_kwargs is None:
58 open_kwargs = {}
---> 59 rds = _io.open_rasterio(
60 filename_or_obj,
61 parse_coordinates=parse_coordinates,
62 cache=False,
63 lock=lock,
64 masked=masked,
65 mask_and_scale=mask_and_scale,
66 variable=variable,
67 group=group,
68 default_name=default_name,
69 decode_times=decode_times,
70 decode_timedelta=decode_timedelta,
71 band_as_variable=band_as_variable,
72 **open_kwargs,
73 )
74 if isinstance(rds, xarray.DataArray):
75 dataset = rds.to_dataset()
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/rioxarray/_io.py:1140, in open_rasterio(filename, parse_coordinates, chunks, cache, lock, masked, mask_and_scale, variable, group, default_name, decode_times, decode_timedelta, band_as_variable, **open_kwargs)
1138 else:
1139 manager = URIManager(file_opener, filename, mode="r", kwargs=open_kwargs)
-> 1140 riods = manager.acquire()
1141 captured_warnings = rio_warnings.copy()
1143 # raise the NotGeoreferencedWarning if applicable
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/file_manager.py:201, in CachingFileManager.acquire(self, needs_lock)
186 def acquire(self, needs_lock: bool = True) -> T_File:
187 """Acquire a file object from the manager.
188
189 A new file is only opened if it has expired from the
(...) 199 An open file object, as returned by ``opener(*args, **kwargs)``.
200 """
--> 201 file, _ = self._acquire_with_cache_info(needs_lock)
202 return file
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/file_manager.py:225, in CachingFileManager._acquire_with_cache_info(self, needs_lock)
223 kwargs = kwargs.copy()
224 kwargs["mode"] = self._mode
--> 225 file = self._opener(*self._args, **kwargs)
226 if self._mode == "w":
227 # ensure file doesn't get overridden when opened again
228 self._mode = "a"
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/rasterio/env.py:464, in ensure_env_with_credentials.<locals>.wrapper(*args, **kwds)
461 session = DummySession()
463 with env_ctor(session=session):
--> 464 return f(*args, **kwds)
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/rasterio/__init__.py:367, in open(fp, mode, driver, width, height, count, crs, transform, dtype, nodata, sharing, thread_safe, opener, **kwargs)
364 path = _parse_path(raw_dataset_path)
366 if mode == "r":
--> 367 dataset = DatasetReader(path, driver=driver, sharing=sharing, thread_safe=thread_safe, **kwargs)
368 elif mode == "r+":
369 dataset = get_writer_for_path(path, driver=driver)(
370 path, mode, driver=driver, sharing=sharing, **kwargs
371 )
File rasterio/_base.pyx:329, in rasterio._base.DatasetBase.__init__()
--> 329 'Could not get source, probably due dynamically evaluated source code.'
RasterioIOError: HTTP response code: 401
Advanced Search#
Optical imagery often has estimates of cloud-cover in the metadata. This is typically a single percentage value for the entire scene footprint. The Vantor API goes a step further allowing for filtering cloudcover for only the AOI rather than the entire scene footprint.

Activating the “area_based_calc” search feature through coincident requires modifying the default “vantor” dataset class. Once activated, in addition to ‘eo:cloud_cover’ ‘view:off_nadir’ metadata will include ‘area:cloud_cover_percentage’ and ‘area:avg_off_nadir_angle’
Note that these searches will take longer!
vantor = coincident.datasets.vantor.Stereo()
vantor
Stereo(alias='vantor', has_stac_api=True, collections=['wv01', 'wv02', 'wv03-vnir', 'ge01'], search='https://api.maxar.com/discovery/v1/search', start='2007-01-01', end=None, spatial_ref=None, type='stereo', provider='vantor', provider_docs=None, stac_kwargs={'limit': 1000}, area_based_calc=False)
# Use AOI-based estimates of cloud-cover instead of entire scene
vantor.area_based_calc = True
gf = coincident.search.search(
dataset=vantor,
bbox=bbox,
datetime=["2021-03-08", "2021-04-01"],
)
cols = [
"title",
"eo:cloud_cover",
"area:cloud_cover_percentage",
"view:off_nadir",
"area:avg_off_nadir_angle",
]
gf[cols]
| title | eo:cloud_cover | area:cloud_cover_percentage | view:off_nadir | area:avg_off_nadir_angle | |
|---|---|---|---|---|---|
| 1 | Maxar WV03 Image 104001006724D500 | 51.883057 | 56.321858 | 28.113903 | 27.457075 |
| 2 | Maxar WV03 Image 1040010067B67700 | 53.269228 | 56.163321 | 27.786872 | 27.291414 |
| 4 | Maxar WV01 Image 10200100A7501F00 | 0.030452 | 0.106177 | 31.826101 | 29.858223 |
| 5 | Maxar WV01 Image 10200100A7DB4600 | 0.008860 | 0.029973 | 27.381504 | 27.601220 |
| 6 | Maxar WV02 Image 10300100BB606C00 | 99.929227 | 99.509544 | 29.767852 | 28.523544 |
| 7 | Maxar WV02 Image 10300100BB8EC300 | 99.952654 | 99.686856 | 24.006449 | 23.991434 |
# Plot with cloud-cover
items = coincident.search.to_pystac_items(gf)
item = items[0]
ax = coincident.plot.plot_vantor_browse(item, overview_level=1)
# Add AOI
rect = plt.Rectangle(
(bbox[0], bbox[1]),
bbox[2] - bbox[0],
bbox[3] - bbox[1],
linewidth=2,
edgecolor="magenta",
facecolor="none",
)
ax.add_patch(rect)
plt.autoscale()
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/file_manager.py:219, in CachingFileManager._acquire_with_cache_info(self, needs_lock)
218 try:
--> 219 file = self._cache[self._key]
220 except KeyError:
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/lru_cache.py:56, in LRUCache.__getitem__(self, key)
55 with self._lock:
---> 56 value = self._cache[key]
57 self._cache.move_to_end(key)
KeyError: [<function open at 0x7f8457e483b0>, ('https://api.maxar.com/discovery/v1/catalogs/imagery/collections/wv03-vnir/items/104001006724D500/assets/collections/dg-archive/assets/browse/104001006724D500.browse.tif',), 'r', (('overview_level', 1), ('sharing', False)), 'fc3de13b-2698-4f61-9376-08479097e128']
During handling of the above exception, another exception occurred:
CPLE_HttpResponseError Traceback (most recent call last)
File rasterio/_base.pyx:320, in rasterio._base.DatasetBase.__init__()
--> 320 'Could not get source, probably due dynamically evaluated source code.'
File rasterio/_base.pyx:232, in rasterio._base.open_dataset()
--> 232 'Could not get source, probably due dynamically evaluated source code.'
File rasterio/_err.pyx:359, in rasterio._err.exc_wrap_pointer()
--> 359 'Could not get source, probably due dynamically evaluated source code.'
CPLE_HttpResponseError: HTTP response code: 401
During handling of the above exception, another exception occurred:
RasterioIOError Traceback (most recent call last)
Cell In[11], line 5
1 # Plot with cloud-cover
2 items = coincident.search.to_pystac_items(gf)
3 item = items[0]
4
----> 5 ax = coincident.plot.plot_vantor_browse(item, overview_level=1)
6 # Add AOI
7 rect = plt.Rectangle(
8 (bbox[0], bbox[1]),
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/src/coincident/plot/matplotlib.py:139, in plot_vantor_browse(item, ax, overview_level)
122 def plot_vantor_browse(
123 item: pystac.Item, ax: plt.Axes | None = None, overview_level: int = 0
124 ) -> plt.Axes:
125 """
126 Map view of Vantor browse image from a STAC item using Matplotlib.
127
(...) 137 The Matplotlib Axes object with the plot.
138 """
--> 139 da = open_vantor_browse(item, overview_level=overview_level)
141 if ax is None:
142 _, ax = plt.subplots(figsize=(8, 11))
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/src/coincident/io/xarray.py:180, in open_vantor_browse(item, overview_level)
175 env = rasterio.Env(
176 GDAL_DISABLE_READDIR_ON_OPEN="EMPTY_DIR",
177 GDAL_HTTP_HEADERS=f"VANTOR-API-KEY:{os.environ['VANTOR_API_KEY']}",
178 )
179 with env:
--> 180 return xr.open_dataarray(
181 href,
182 engine="rasterio",
183 mask_and_scale=False, # otherwise uint8 -> float32!
184 backend_kwargs={"open_kwargs": {"overview_level": overview_level}},
185 )
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/api.py:814, in open_dataarray(filename_or_obj, engine, chunks, cache, decode_cf, mask_and_scale, decode_times, decode_timedelta, use_cftime, concat_characters, decode_coords, drop_variables, create_default_indexes, inline_array, chunked_array_type, from_array_kwargs, backend_kwargs, **kwargs)
631 def open_dataarray(
632 filename_or_obj: T_PathFileOrDataStore,
633 *,
(...) 652 **kwargs,
653 ) -> DataArray:
654 """Open a DataArray from a file or file-like object containing a single
655 data variable.
656
(...) 811 open_dataset
812 """
--> 814 dataset = open_dataset(
815 filename_or_obj,
816 decode_cf=decode_cf,
817 mask_and_scale=mask_and_scale,
818 decode_times=decode_times,
819 concat_characters=concat_characters,
820 decode_coords=decode_coords,
821 engine=engine,
822 chunks=chunks,
823 cache=cache,
824 drop_variables=drop_variables,
825 create_default_indexes=create_default_indexes,
826 inline_array=inline_array,
827 chunked_array_type=chunked_array_type,
828 from_array_kwargs=from_array_kwargs,
829 backend_kwargs=backend_kwargs,
830 use_cftime=use_cftime,
831 decode_timedelta=decode_timedelta,
832 **kwargs,
833 )
835 if len(dataset.data_vars) != 1:
836 if len(dataset.data_vars) == 0:
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/api.py:607, in open_dataset(filename_or_obj, engine, chunks, cache, decode_cf, mask_and_scale, decode_times, decode_timedelta, use_cftime, concat_characters, decode_coords, drop_variables, create_default_indexes, inline_array, chunked_array_type, from_array_kwargs, backend_kwargs, **kwargs)
595 decoders = _resolve_decoders_kwargs(
596 decode_cf,
597 open_backend_dataset_parameters=backend.open_dataset_parameters,
(...) 603 decode_coords=decode_coords,
604 )
606 overwrite_encoded_chunks = kwargs.pop("overwrite_encoded_chunks", None)
--> 607 backend_ds = backend.open_dataset(
608 filename_or_obj,
609 drop_variables=drop_variables,
610 **decoders,
611 **kwargs,
612 )
613 ds = _dataset_from_backend_dataset(
614 backend_ds,
615 filename_or_obj,
(...) 626 **kwargs,
627 )
628 return ds
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/rioxarray/xarray_plugin.py:59, in RasterioBackend.open_dataset(self, filename_or_obj, drop_variables, parse_coordinates, lock, masked, mask_and_scale, variable, group, default_name, decode_coords, decode_times, decode_timedelta, band_as_variable, open_kwargs)
57 if open_kwargs is None:
58 open_kwargs = {}
---> 59 rds = _io.open_rasterio(
60 filename_or_obj,
61 parse_coordinates=parse_coordinates,
62 cache=False,
63 lock=lock,
64 masked=masked,
65 mask_and_scale=mask_and_scale,
66 variable=variable,
67 group=group,
68 default_name=default_name,
69 decode_times=decode_times,
70 decode_timedelta=decode_timedelta,
71 band_as_variable=band_as_variable,
72 **open_kwargs,
73 )
74 if isinstance(rds, xarray.DataArray):
75 dataset = rds.to_dataset()
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/rioxarray/_io.py:1140, in open_rasterio(filename, parse_coordinates, chunks, cache, lock, masked, mask_and_scale, variable, group, default_name, decode_times, decode_timedelta, band_as_variable, **open_kwargs)
1138 else:
1139 manager = URIManager(file_opener, filename, mode="r", kwargs=open_kwargs)
-> 1140 riods = manager.acquire()
1141 captured_warnings = rio_warnings.copy()
1143 # raise the NotGeoreferencedWarning if applicable
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/file_manager.py:201, in CachingFileManager.acquire(self, needs_lock)
186 def acquire(self, needs_lock: bool = True) -> T_File:
187 """Acquire a file object from the manager.
188
189 A new file is only opened if it has expired from the
(...) 199 An open file object, as returned by ``opener(*args, **kwargs)``.
200 """
--> 201 file, _ = self._acquire_with_cache_info(needs_lock)
202 return file
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/xarray/backends/file_manager.py:225, in CachingFileManager._acquire_with_cache_info(self, needs_lock)
223 kwargs = kwargs.copy()
224 kwargs["mode"] = self._mode
--> 225 file = self._opener(*self._args, **kwargs)
226 if self._mode == "w":
227 # ensure file doesn't get overridden when opened again
228 self._mode = "a"
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/rasterio/env.py:464, in ensure_env_with_credentials.<locals>.wrapper(*args, **kwds)
461 session = DummySession()
463 with env_ctor(session=session):
--> 464 return f(*args, **kwds)
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/rasterio/__init__.py:367, in open(fp, mode, driver, width, height, count, crs, transform, dtype, nodata, sharing, thread_safe, opener, **kwargs)
364 path = _parse_path(raw_dataset_path)
366 if mode == "r":
--> 367 dataset = DatasetReader(path, driver=driver, sharing=sharing, thread_safe=thread_safe, **kwargs)
368 elif mode == "r+":
369 dataset = get_writer_for_path(path, driver=driver)(
370 path, mode, driver=driver, sharing=sharing, **kwargs
371 )
File rasterio/_base.pyx:329, in rasterio._base.DatasetBase.__init__()
--> 329 'Could not get source, probably due dynamically evaluated source code.'
RasterioIOError: HTTP response code: 401
Load cloud-cover polygon#
For each acquisition, Vantor includes a Multipolygon that estimates cloud cover. Behind the scenes, coincident uses the stac-asset library to read and download assets of any STAC item. Because stac-asset uses asynchronous functions, you have to use await to return results.
bytes = await coincident.io.download.read_href(item, "cloud-cover")
gf_clouds = gpd.read_file(bytes)
gf_clouds.explore()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[12], line 1
----> 1 bytes = await coincident.io.download.read_href(item, "cloud-cover")
2 gf_clouds = gpd.read_file(bytes)
3 gf_clouds.explore()
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/src/coincident/io/download.py:100, in read_href(item, asset, config)
72 """
73 Open and read a STAC asset href into local memory
74
(...) 96 gf_clouds = gpd.read_file(bytes)
97 """
98 href = item.assets.get(asset).href
--> 100 config = _set_config(item, config)
102 return await stac_asset.read_href(href, config=config)
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/src/coincident/io/download.py:62, in _set_config(item, config)
57 config = stac_asset.Config(
58 earthdata_token=os.environ.get("EARTHDATA_TOKEN"),
59 exclude=exclude_keys,
60 )
61 else:
---> 62 config = stac_asset.Config(**config)
64 return config
TypeError: stac_asset.config.Config() argument after ** must be a mapping, not NoneType
Download browse image + metadata#
coincident wraps the stac-asset library to easily download local copies of a STAC item and all of its assets (e.g. metadata and cloud cover mask)
# Set download directory to match item STAC ID
download_dir = f"/tmp/{item.id}"
local_item = await coincident.io.download.download_item(item, download_dir)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[13], line 3
1 # Set download directory to match item STAC ID
2 download_dir = f"/tmp/{item.id}"
----> 3 local_item = await coincident.io.download.download_item(item, download_dir)
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/src/coincident/io/download.py:138, in download_item(item, path, config)
110 """
111 Downloads a STAC item to a specified local path.
112
(...) 134 localitem = asyncio.run(download_item(item))
135 """
136 posixpath = Path(path)
--> 138 config = _set_config(item, config)
140 # NOTE: prevent in-place modification of remote hrefs with item.clone()
141 return await stac_asset.download_item(item.clone(), posixpath, config=config)
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/src/coincident/io/download.py:62, in _set_config(item, config)
57 config = stac_asset.Config(
58 earthdata_token=os.environ.get("EARTHDATA_TOKEN"),
59 exclude=exclude_keys,
60 )
61 else:
---> 62 config = stac_asset.Config(**config)
64 return config
TypeError: stac_asset.config.Config() argument after ** must be a mapping, not NoneType
!ls {download_dir}
ls: cannot access '/tmp/104001006724D500': No such file or directory