Skip to content

Commit bb851b5

Browse files
committed
add vhi to client
1 parent 6f0ebc6 commit bb851b5

File tree

3 files changed

+83
-2
lines changed

3 files changed

+83
-2
lines changed

dweather_client/aliases_and_units.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
"degC": u.deg_C,
2020
"m s**-1": u.m / u.s,
2121
"degF": imperial.deg_F,
22-
"m of water equivalent": u.m
22+
"m of water equivalent": u.m,
23+
"vegetative health score": u.dimensionless_unscaled
2324
}
2425

2526
METRIC_TO_IMPERIAL = {

dweather_client/gridded_datasets.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
from dweather_client.ipfs_queries import SimpleGriddedDataset, RtmaGriddedDataset, Era5LandWind, PrismGriddedDataset
1+
from dweather_client.ipfs_queries import SimpleGriddedDataset, RtmaGriddedDataset, Era5LandWind, PrismGriddedDataset, Vhi
22

33
"""
44
Module containing the leaf classes for the gridded dataset inheritance chain.
55
Do not add non-leaf classes to this file, as it will cause the dict built from this module to break
6+
"Unused" `Vhi` import is intentional and necessary, as list of gridded datasets is built from all classes in this file!
67
"""
78

89
class PrismcTmaxDaily(PrismGriddedDataset):

dweather_client/ipfs_queries.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ def get_data(self, lat, lon):
367367
ret_lat, ret_lon = cpc_lat_lon_to_conventional(self.snapped_lat, self.snapped_lon)
368368
return (float(ret_lat), float(ret_lon)), pd.Series(ret_dict)
369369

370+
370371
class Era5LandWind(SimpleGriddedDataset):
371372
"""
372373
Abstract class from which ERA5 Land Wind datasets inherit. Sets formatting that these datasets use
@@ -375,6 +376,84 @@ class Era5LandWind(SimpleGriddedDataset):
375376
def zero_padding(self):
376377
return 8
377378

379+
class Vhi(IpfsDataset):
380+
"""
381+
Instantiable gridded vegetative health dataset. Due to some metadata differences with other sets, doesn't
382+
inherit from GriddedDataset
383+
"""
384+
dataset = "vhi"
385+
NUM_NAS_AT_START_OF_DATA = 34
386+
387+
def get_data(self, lat, lon):
388+
super().get_data()
389+
first_metadata = self.get_metadata(self.head)
390+
snapped_lat, snapped_lon = self.snap_to_grid(float(lat), float(lon), first_metadata)
391+
self.zip_file_name = f"{snapped_lat:.3f}.zip"
392+
self.gzip_name = f"{snapped_lat:.3f}_{snapped_lon:.3f}.gz"
393+
hashes = self.traverse_ll(self.head)
394+
395+
ret_dict = {}
396+
for h in hashes:
397+
date_range = self.get_date_range_from_metadata(h)
398+
weather_dict = self.get_weather_dict(date_range, h)
399+
ret_dict = {**ret_dict, **weather_dict}
400+
401+
return (snapped_lat, snapped_lon), pd.Series(ret_dict).iloc[self.NUM_NAS_AT_START_OF_DATA:]
402+
403+
def get_weather_dict(self, date_range, ipfs_hash):
404+
"""
405+
Uses a weekly time span, so logic is a little different from other datasets
406+
"""
407+
with zipfile.ZipFile(self.get_file_object(f"{ipfs_hash}/{self.zip_file_name}")) as zi:
408+
with gzip.open(zi.open(self.gzip_name)) as gz:
409+
cell_text = gz.read().decode('utf-8')
410+
vhi_dict = {}
411+
year = date_range[0].year
412+
for year_data in cell_text.split('\n'):
413+
if year == date_range[0].year:
414+
date_itr = date_range[0]
415+
else:
416+
date_itr = datetime.date(year, 1, 1)
417+
for week_data in year_data.split(','):
418+
vhi_dict[date_itr] = "-999" if week_data == "-999.00" else week_data
419+
date_itr += datetime.timedelta(days=7)
420+
year += 1
421+
return vhi_dict
422+
423+
@classmethod
424+
def snap_to_grid(cls, lat, lon, metadata):
425+
"""
426+
Find the nearest (lat,lon) on IPFS for a VHI metadata file.
427+
args:
428+
:lat: = -90 < lat < 90, float
429+
:lon: = -180 < lon < 180, float
430+
:metadata: a dWeather metadata file
431+
return: lat, lon
432+
Necessary to rewrite because the files' coordinates correspond to the center of each gridcell, not the northwest corner
433+
"""
434+
435+
# metadata measures from center, not left edge
436+
resolution = metadata['resolution']
437+
min_lat = metadata['latitude range'][0] + resolution / 2 # start [lat, lon]
438+
min_lon = metadata['longitude range'][0] + resolution / 2 # end [lat, lon]
439+
440+
# check that the lat lon is in the bounding box
441+
snap_lat = round(round((lat - min_lat) / resolution) * resolution + min_lat, 3)
442+
snap_lon = round(round((lon - min_lon) / resolution) * resolution + min_lon, 3)
443+
return snap_lat, snap_lon
444+
445+
def get_date_range_from_metadata(self, h):
446+
"""
447+
args:
448+
:h: hash for ipfs directory containing metadata
449+
return: list of [start_time, end_time]
450+
"""
451+
metadata = self.get_metadata(h)
452+
# first year is filled with -999s, so have to start from 1981-01-01
453+
start_date = "1981-01-01" if metadata["date range"][0] == "1981-08-28" else metadata["date range"][0]
454+
end_date = metadata["date range"][1]
455+
return [datetime.date.fromisoformat(dt) for dt in [start_date, end_date]]
456+
378457
class StationDataset(IpfsDataset):
379458
"""
380459
Instantiable class used for pulling in "ghcnd" or "ghcnd-imputed-daily" station data

0 commit comments

Comments
 (0)