G-LiHT#
This notebook illustrates search, directly loading DEMs, and downloading DEMs and LPCs for local processing
Dataset |
Alias |
Type |
Start |
End |
Extent |
Source |
|---|---|---|---|---|---|---|
NASA G-LiHT |
gliht |
LiDAR |
2011-07-28 |
2020-03-12 |
US Territories |
import coincident
import geopandas as gpd
from shapely.geometry import box
import matplotlib.pyplot as plt
# %config InlineBackend.figure_format = 'retina'
/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
Matplotlib is building the font cache; this may take a moment.
Search#
We’ll search for data in Maryland in July 2017
aoi = gpd.read_file(
"https://raw.githubusercontent.com/unitedstates/districts/refs/heads/gh-pages/states/MD/shape.geojson"
)
aoi = aoi.simplify(0.01)
m = aoi.explore(color="black")
gf = coincident.search.search(
dataset="gliht",
intersects=aoi,
datetime="2017-07",
)
print(f"Found {len(gf)} G-LiHT Items")
print(f"G-LiHT collections: {gf.collection.unique()}")
gf.explore(m=m, color="magenta")
Found 61 G-LiHT Items
G-LiHT collections: ['GLCHMT_001', 'GLDTMT_001', 'GLDSMT_001', 'GLLIDARPC_001']
Categories (4, str): ['GLCHMT_001', 'GLDTMT_001', 'GLDSMT_001', 'GLLIDARPC_001']
Get Data#
NASA G-LiHT has many different provided gridded datasets. The following collections below are the current datasets supported by coincident.
Collection |
Description |
|---|---|
GLORTHO_001 |
orthorectified high-resolution aerial photography |
GLCHMT_001 |
maximum canopy height and canopy variability information |
GLDSMT_001 |
Digital Surface Model, Mean, Aspect, Rugosity, and Slope |
GLDTMT_001 |
bare earth elevation, aspect and slope on the EGM96 Geoid |
GLLIDARPC_001 |
LiDAR Point Cloud data product (LAS format) |
Note
Not all G-LiHT flights will contain every single product listed. For example, a flight may have ‘dsm’ data but not ‘ortho’ data.
# Subset a particular scene
collection = "GLDTMT_001"
gf_gliht = gf.query(f'collection == "{collection}"')
gf_gliht.id
10 GLDTMT_SERC_CalTarps_31July2017_am_l9s0_DTM
11 GLDTMT_SERC_CalTarps_31July2017_am_l8s0_DTM
12 GLDTMT_SERC_CalTarps_31July2017_am_l6s0_DTM
13 GLDTMT_SERC_CalTarps_31July2017_am_l2s0_DTM
14 GLDTMT_SERC_CalTarps_31July2017_am_l7s0_DTM
15 GLDTMT_SERC_CalTarps_31July2017_am_l3s0_DTM
16 GLDTMT_SERC_CalTarps_31July2017_am_l1s0_DTM
17 GLDTMT_SERC_CalTarps_31July2017_am_l5s0_DTM
18 GLDTMT_SERC_CalTarps_31July2017_am_l4s0_DTM
19 GLDTMT_SERC_CalTarps_31July2017_am_l0s0_DTM
41 GLDTMT_SERC_ForestGEO_31July2017_am_DTM
Name: id, dtype: str
# Just use the first one
gs_item = gf_gliht.iloc[0]
c = gs_item.geometry.centroid
mini_aoi = gpd.GeoDataFrame(
geometry=[box(c.x - 0.0045, c.y - 0.0045, c.x + 0.0045, c.y + 0.0045)],
crs="EPSG:4326",
)
m = gf_gliht.explore(column="id")
mini_aoi.explore(m=m, color="red", style_kwds={"fill": False, "weight": 3})
Stream#
First we’ll show how to stream a subset of a gridded DSM directly into Xarray
Important
Unlike the G-LiHT search, you will need NASA Earthdata credentials (aka EarthData Login (EDL)) to read in and download the gridded datasets from G-LiHT. This requires creating an EDL Token and making sure you’ve set the environment variable EARTHDATA_TOKEN=xxxxx
%%time
da = coincident.io.xarray.load_gliht(
item=gs_item,
aoi=mini_aoi,
)
da
CPU times: user 3.65 ms, sys: 2.08 ms, total: 5.74 ms
Wall time: 624 ms
---------------------------------------------------------------------------
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 0x75a86e8b6820>, ('https://data.lpdaac.earthdatacloud.nasa.gov/lp-prod-protected/GLDTMT.001/GLDTMT_SERC_CalTarps_31July2017_am_l9s0_DTM/GLDTMT_SERC_CalTarps_31July2017_am_l9s0_DTM.tif',), 'r', (('sharing', False),), '05906d13-38bd-46ef-b92d-418f8f587f58']
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 - Failure writing output to destination, passed 77 returned 0
During handling of the above exception, another exception occurred:
RasterioIOError Traceback (most recent call last)
Cell In[6], line 1
----> 1 get_ipython().run_cell_magic('time', '', '\nda = coincident.io.xarray.load_gliht(\n item=gs_item,\n aoi=mini_aoi,\n)\nda\n')
File <timed exec>:1
----> 1 'Could not get source, probably due dynamically evaluated source code.'
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/src/coincident/io/xarray.py:763, in load_gliht(item, aoi, mask, **kwargs)
753 # Use a rasterio.Env context to handle authentication for remote NASA access
754 with rasterio.Env(
755 GDAL_DISABLE_READDIR_ON_OPEN="EMPTY_DIR",
756 CPL_VSIL_CURL_USE_HEAD="NO",
(...) 761 GDAL_HTTP_COOKIEJAR="/tmp/cookies.txt",
762 ):
--> 763 da = rioxarray.open_rasterio(
764 data_assets[asset_keys_to_load[0]]["href"],
765 masked=mask,
766 **kwargs,
767 )
768 da = da.rename({"band": "time"})
769 da["time"] = item.datetime
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 - Failure writing output to destination, passed 77 returned 0
coincident.plot.plot_dem(da)
plt.title(gs_item.id);
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[7], line 1
----> 1 coincident.plot.plot_dem(da)
2 plt.title(gs_item.id);
NameError: name 'da' is not defined
Download#
Download multiple STAC Item Assets#
This will put STAC metadata, extended metadata, a browse/thumbnail image, and a TIF in a specified folder
# back to a GeoDataFrame from a Series
gfi = gpd.GeoDataFrame([gs_item])
# And to a Pystac Item
items = coincident.search.to_pystac_items(gfi)
items[0]
# Download the item assets (browse, metadata, thumbnail)
local_item = await coincident.io.download.download_item(
items[0],
path="/tmp/gliht/",
)
---------------------------------------------------------------------------
DownloadError Traceback (most recent call last)
Cell In[9], line 2
1 # Download the item assets (browse, metadata, thumbnail)
----> 2 local_item = await coincident.io.download.download_item(
3 items[0],
4 path="/tmp/gliht/",
5 )
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/src/coincident/io/download.py:141, in download_item(item, path, config)
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/.pixi/envs/docs/lib/python3.14/site-packages/stac_asset/_functions.py:254, in download_item(item, directory, file_name, infer_file_name, config, messages, clients, keep_non_downloaded, max_concurrent_downloads, stream)
248 async with Downloads(
249 config=config or Config(),
250 clients=clients,
251 max_concurrent_downloads=max_concurrent_downloads,
252 ) as downloads:
253 await downloads.add(item, Path(directory), file_name, keep_non_downloaded)
--> 254 await downloads.download(messages, stream)
256 self_href = item.get_self_href()
257 if self_href:
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/stac_asset/_functions.py:173, in Downloads.download(self, messages, stream)
171 exceptions.append(result.error)
172 if exceptions:
--> 173 raise DownloadError(exceptions)
DownloadError: ClientResponseError: 401, message='Unauthorized', url='https://data.lpdaac.earthdatacloud.nasa.gov/lp-prod-protected/GLDTMT.001/GLDTMT_SERC_CalTarps_31July2017_am_l9s0_DTM/GLDTMT_SERC_CalTarps_31July2017_am_l9s0_DTM.tif'
ClientResponseError: 401, message='Unauthorized', url='https://cmr.earthdata.nasa.gov/search/concepts/G2793865577-LPCLOUD.xml'
Download a single file#
tif_key = [x for x in gs_item.assets.keys() if x.startswith("001")][0]
href = gs_item.assets[tif_key]["href"]
coincident.io.download.download_files([href], "/tmp/")
---------------------------------------------------------------------------
HTTPError Traceback (most recent call last)
Cell In[10], line 3
1 tif_key = [x for x in gs_item.assets.keys() if x.startswith("001")][0]
2 href = gs_item.assets[tif_key]["href"]
----> 3 coincident.io.download.download_files([href], "/tmp/")
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/src/coincident/io/download.py:196, in download_files(files, output_dir, product, chunk_size)
193 continue
195 resp = requests.get(url, stream=True, headers=headers, timeout=60)
--> 196 resp.raise_for_status()
197 total = int(resp.headers.get("content-length", 0))
198 inner = tqdm(
199 total=total,
200 unit="iB",
(...) 204 leave=False,
205 )
File ~/checkouts/readthedocs.org/user_builds/coincident/checkouts/stable/.pixi/envs/docs/lib/python3.14/site-packages/requests/models.py:1167, in Response.raise_for_status(self)
1162 http_error_msg = (
1163 f"{self.status_code} Server Error: {reason} for url: {self.url}"
1164 )
1166 if http_error_msg:
-> 1167 raise HTTPError(http_error_msg, response=self)
HTTPError: 401 Client Error: Unauthorized for url: https://data.lpdaac.earthdatacloud.nasa.gov/lp-prod-protected/GLDTMT.001/GLDTMT_SERC_CalTarps_31July2017_am_l9s0_DTM/GLDTMT_SERC_CalTarps_31July2017_am_l9s0_DTM.tif