Skip to content

Commit f92eec2

Browse files
committed
Indexing improvements for ABI projection, navigation, and imagery
- Enable projections for indexed subsets of the ABI Fixed Grid Format (FGF), including Cloud Optimized GeoTIFF (COG), IREMIS, and WaterMask outputs - ABINavigation and ABINaturalRGB objects can now be constrained to an index or continuous slice on the ABI FGF - Fix possible division by zero exception in NCInterface - Fix bug where resample2cog() could not overwrite an existing file
1 parent 2d7fdd4 commit f92eec2

File tree

8 files changed

+388
-153
lines changed

8 files changed

+388
-153
lines changed

heregoes/ancillary.py

+56-37
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# You should have received a copy of the GNU General Public License
1919
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2020

21-
"""Classes for working with ancillary datasets in the ABI fixed grid projection"""
21+
"""Classes for working with ancillary datasets in the ABI Fixed Grid projection"""
2222

2323
import io
2424

@@ -31,10 +31,9 @@
3131
from pathlib import Path
3232

3333
import matplotlib.pyplot as plt
34-
import netCDF4
3534
import numpy as np
3635

37-
from heregoes import exceptions, projection
36+
from heregoes import exceptions, navigation, projection
3837
from heregoes.util import linear_interp, ncinterface
3938

4039
SCRIPT_PATH = Path(__file__).parent.resolve()
@@ -96,7 +95,7 @@ def load(self, npz_path):
9695
class IREMIS(AncillaryDataset):
9796
"""
9897
Loads in UW CIMSS' Baseline Fit Infrared Emissivity Database a.k.a IREMIS (Download and info: https://cimss.ssec.wisc.edu/iremis/)
99-
and projects to the ABI fixed grid. Requres IREMIS netCDFs to be present in a directory set by the `iremis_dir` argument or
98+
and projects to the ABI Fixed Grid. Requres IREMIS netCDFs to be present in a directory set by the `iremis_dir` argument or
10099
by the HEREGOES_ENV_IREMIS_DIR environmental variable.
101100
102101
Provides a linear interpolation of land surface emissivity for:
@@ -105,16 +104,21 @@ class IREMIS(AncillaryDataset):
105104
106105
Arguments:
107106
- `abi_data`: The ABIObject formed on a GOES-R ABI L1b Radiance netCDF file as returned by `heregoes.load()`
107+
- `index`: Optionally constrains the IREMIS dataset to an index or continuous slice on the ABI Fixed Grid matching the resolution of the provided `abi_data` object
108108
- `iremis_dir`: Location of IREMIS netCDF files. Defaults to the directory set by the HEREGOES_ENV_IREMIS_DIR environmental variable
109109
"""
110110

111-
def __init__(self, abi_data, iremis_dir=IREMIS_DIR):
111+
def __init__(self, abi_data, index=None, iremis_dir=IREMIS_DIR):
112112
super().__init__()
113113

114114
self.abi_data = abi_data
115+
self.index = index
115116
month = self.abi_data.time_coverage_start.month
116117
self.dataset_name = "iremis_month" + str(month).zfill(2)
117118

119+
if self.index is None:
120+
self.index = np.s_[:, :]
121+
118122
try:
119123
iremis_dir = Path(iremis_dir)
120124
except Exception as e:
@@ -124,7 +128,7 @@ def __init__(self, abi_data, iremis_dir=IREMIS_DIR):
124128
exception=e,
125129
)
126130

127-
iremis_locations = iremis_dir.joinpath("global_emis_inf10_location.nc")
131+
iremis_locations_nc = iremis_dir.joinpath("global_emis_inf10_location.nc")
128132
iremis_months = [
129133
"global_emis_inf10_monthFilled_MYD11C3.A2016001.041.nc",
130134
"global_emis_inf10_monthFilled_MYD11C3.A2016032.041.nc",
@@ -176,13 +180,13 @@ def __init__(self, abi_data, iremis_dir=IREMIS_DIR):
176180
np.rot90(self.data["c14_land_emissivity"], k=1)
177181
)
178182

179-
with netCDF4.Dataset(iremis_locations, "r") as iremis_locations_nc:
180-
iremis_ul_lat = iremis_locations_nc["lat"][0, 0]
181-
iremis_ul_lon = iremis_locations_nc["lon"][0, 0]
182-
iremis_lr_lat = iremis_locations_nc["lat"][-1, -1]
183-
iremis_lr_lon = iremis_locations_nc["lon"][-1, -1]
183+
iremis_locations = ncinterface.NCInterface(iremis_locations_nc)
184+
iremis_ul_lat = iremis_locations["lat"][0, 0].item()
185+
iremis_ul_lon = iremis_locations["lon"][0, 0].item()
186+
iremis_lr_lat = iremis_locations["lat"][-1, -1].item()
187+
iremis_lr_lon = iremis_locations["lon"][-1, -1].item()
184188

185-
abi_projection = projection.ABIProjection(self.abi_data)
189+
abi_projection = projection.ABIProjection(self.abi_data, index=self.index)
186190
self.data["c07_land_emissivity"] = abi_projection.resample2abi(
187191
self.data["c07_land_emissivity"],
188192
latlon_bounds=[iremis_ul_lon, iremis_ul_lat, iremis_lr_lon, iremis_lr_lat],
@@ -198,23 +202,39 @@ def __init__(self, abi_data, iremis_dir=IREMIS_DIR):
198202
class WaterMask(AncillaryDataset):
199203
"""
200204
Loads in Global Self-consistent, Hierarchical, High-resolution Shorelines (GSHHS a.k.a GSHHG) (http://www.soest.hawaii.edu/pwessel/gshhg/)
201-
and projects to the ABI fixed grid. Natural Earth rivers (https://www.naturalearthdata.com/downloads/10m-physical-vectors/10m-rivers-lake-centerlines/)
205+
and projects to the ABI Fixed Grid. Natural Earth rivers (https://www.naturalearthdata.com/downloads/10m-physical-vectors/10m-rivers-lake-centerlines/)
202206
are optionally added on top of GSHHS when `rivers` is `True`. Both the GSHHS and Natural Earth datasets are automatically downloaded by Cartopy.
203207
204208
Provides a boolean land/water mask in `data['water_mask']` where water is `False` and land is `True`.
205209
206210
Arguments:
207211
- `abi_data`: The ABIObject formed on a GOES-R ABI L1b Radiance netCDF file as returned by `heregoes.load()`
212+
- `index`: Optionally constrains the GSHHS dataset to an index or continuous slice on the ABI Fixed Grid matching the resolution of the provided `abi_data` object
208213
- `gshhs_scale`: 'auto', 'coarse', 'low', 'intermediate', 'high, or 'full' (https://scitools.org.uk/cartopy/docs/latest/reference/generated/cartopy.feature.GSHHSFeature.html)
209214
- `rivers`: Default `False`
210215
"""
211216

212-
def __init__(self, abi_data, gshhs_scale="intermediate", rivers=False):
217+
def __init__(
218+
self,
219+
abi_data,
220+
index=None,
221+
gshhs_scale="intermediate",
222+
rivers=False,
223+
):
213224
super().__init__()
214225

215226
self.abi_data = abi_data
227+
self.index = index
216228
self.dataset_name = "gshhs_" + gshhs_scale
217229

230+
if self.index is None:
231+
self.index = np.s_[:, :]
232+
233+
y_rad = self.abi_data["y"][self.index[0]]
234+
x_rad = self.abi_data["x"][self.index[1]]
235+
236+
self._abi_height, self._abi_width = y_rad.size, x_rad.size
237+
218238
# https://scitools.org.uk/cartopy/docs/latest/crs/index.html#cartopy.crs.Globe
219239
goes_globe = ccrs.Globe(
220240
datum=None,
@@ -245,32 +265,31 @@ def __init__(self, abi_data, gshhs_scale="intermediate", rivers=False):
245265
dpi = 1000
246266
plt.figure(
247267
figsize=(
248-
self.abi_data.dimensions["x"].size / dpi,
249-
self.abi_data.dimensions["y"].size / dpi,
268+
self._abi_width / dpi,
269+
self._abi_height / dpi,
250270
),
251271
dpi=dpi,
252272
)
253273
ax = plt.axes(projection=goes_projection)
254274

255-
# cartopy errors on extents the size of the ABI Full Disk
256-
if self.abi_data.scene_id != "Full Disk":
257-
ul_x = (
258-
self.abi_data["x_image_bounds"][0]
259-
* self.abi_data["goes_imager_projection"].perspective_point_height
260-
)
261-
ul_y = (
262-
self.abi_data["y_image_bounds"][0]
263-
* self.abi_data["goes_imager_projection"].perspective_point_height
264-
)
265-
lr_x = (
266-
self.abi_data["x_image_bounds"][1]
267-
* self.abi_data["goes_imager_projection"].perspective_point_height
268-
)
269-
lr_y = (
270-
self.abi_data["y_image_bounds"][1]
271-
* self.abi_data["goes_imager_projection"].perspective_point_height
272-
)
273-
ax.set_extent([ul_x, lr_x, ul_y, lr_y], crs=goes_projection)
275+
h = self.abi_data["goes_imager_projection"].perspective_point_height
276+
277+
# the full scanning angle extents are given by the gridded projection coordinates padded by half of the pixel IFOV on all sides
278+
ul_x = (
279+
(np.atleast_1d(x_rad[0]) - (self.abi_data.resolution_ifov / 2)) * h
280+
).item()
281+
ul_y = (
282+
(np.atleast_1d(y_rad[0]) + (self.abi_data.resolution_ifov / 2)) * h
283+
).item()
284+
lr_x = (
285+
(np.atleast_1d(x_rad[-1]) + (self.abi_data.resolution_ifov / 2)) * h
286+
).item()
287+
lr_y = (
288+
(np.atleast_1d(y_rad[-1]) - (self.abi_data.resolution_ifov / 2)) * h
289+
).item()
290+
291+
ax.set_ylim([lr_y, ul_y])
292+
ax.set_xlim([ul_x, lr_x])
274293

275294
# https://scitools.org.uk/cartopy/docs/v0.14/matplotlib/feature_interface.html#cartopy.feature.GSHHSFeature
276295
gshhs_coastline = cf.GSHHSFeature(
@@ -338,8 +357,8 @@ def __init__(self, abi_data, gshhs_scale="intermediate", rivers=False):
338357
self.data["water_mask"] = np.reshape(
339358
np.frombuffer(io_buf.getvalue(), dtype=np.uint8),
340359
newshape=(
341-
self.abi_data.dimensions["y"].size,
342-
self.abi_data.dimensions["x"].size,
360+
self._abi_height,
361+
self._abi_width,
343362
-1,
344363
),
345364
)

heregoes/goesr/__init__.py

+8-6
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
import datetime
2424

25+
import numpy as np
26+
2527
from heregoes.goesr import coefficients
2628
from heregoes.util.ncinterface import NCInterface
2729

@@ -85,14 +87,14 @@ def __init__(self, *args, **kwargs):
8587
self.scene_id_safe = "CONUS"
8688

8789
if self["Rad"].resolution == "y: 0.000014 rad x: 0.000014 rad":
88-
self.resolution_ifov = 14.0e-6
89-
self.resolution_km = 0.5
90+
self.resolution_ifov = np.array(14.0e-6, dtype=np.float32)
91+
self.resolution_km = np.array(0.5, dtype=np.float32)
9092
elif self["Rad"].resolution == "y: 0.000028 rad x: 0.000028 rad":
91-
self.resolution_ifov = 28.0e-6
92-
self.resolution_km = 1.0
93+
self.resolution_ifov = np.array(28.0e-6, dtype=np.float32)
94+
self.resolution_km = np.array(1.0, dtype=np.float32)
9395
elif self["Rad"].resolution == "y: 0.000056 rad x: 0.000056 rad":
94-
self.resolution_ifov = 56.0e-6
95-
self.resolution_km = 2.0
96+
self.resolution_ifov = np.array(56.0e-6, dtype=np.float32)
97+
self.resolution_km = np.array(2.0, dtype=np.float32)
9698

9799
self.band_id_safe = "C" + str(self["band_id"][...].item()).zfill(2)
98100

0 commit comments

Comments
 (0)