Skip to content

Commit 27bd449

Browse files
committed
Move geodata load outside for loop
1 parent 7a2ed9e commit 27bd449

File tree

3 files changed

+85
-120
lines changed

3 files changed

+85
-120
lines changed

coastlines/vector.py

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -190,24 +190,38 @@ def load_rasters(
190190
# return ocean_mask
191191

192192

193-
def ocean_masking(ds, connectivity=1, dilation=None, land_dilation=10):
193+
def ocean_masking(ds, ocean_land_da, connectivity=1, dilation=None, land_dilation=10):
194194
"""
195195
Identifies ocean by selecting regions of water that overlap
196-
with ocean pixels in the Geodata Coast 100K layer.
196+
with ocean pixels. This region can be optionally dilated to
197+
ensure that the sub-pixel algorithm has pixels on either side
198+
of the water index threshold.
197199
198200
Parameters:
199201
-----------
200202
ds : xarray.DataArray
201203
An array containing True for land pixels, and False for water.
202204
This can be obtained by thresholding a water index
203205
array (e.g. MNDWI < 0).
206+
ocean_land_da : xarray.DataArray
207+
A supplementary dataset used to separate ocean waters from
208+
other inland water. The array should contain values of 0
209+
for ocean, and values of 1 or greater for land pixels.
210+
For Australia, we use the Geodata 100K coastline dataset,
211+
rasterized as the "geodata_coast_100k" product on the
212+
DEA datacube.
204213
connectivity : integer, optional
205214
An integer passed to the 'connectivity' parameter of the
206215
`skimage.measure.label` function.
207-
dilation : integer, optional
216+
dilation : integer, optional
208217
The number of pixels to dilate ocean pixels to ensure than
209218
adequate land pixels are included for subpixel waterline
210219
extraction. Defaults to None.
220+
land_dilation : integer, optional
221+
The number of pixels by which to dilate land pixels in
222+
`ocean_land_da`. This ensures that we only select true
223+
deeper ocean pixels when separating inland from ocean
224+
waters.
211225
212226
Returns:
213227
--------
@@ -216,15 +230,8 @@ def ocean_masking(ds, connectivity=1, dilation=None, land_dilation=10):
216230
pixels as True.
217231
"""
218232

219-
dc = datacube.Datacube()
220-
geodata_ds = dc.load(
221-
product="geodata_coast_100k",
222-
like=ds.odc.geobox.compat,
223-
dask_chunks={},
224-
).squeeze("time")
225-
226233
# Identify pixels that are land in either Geodata or ds
227-
land_mask = (geodata_ds.land != 0) | ds
234+
land_mask = (ocean_land_da != 0) | ds
228235

229236
# Dilate to restrict to deeper ocean, and invert to get water
230237
water_mask = ~odc.algo.binary_dilation(land_mask, land_dilation)
@@ -253,20 +260,25 @@ def ocean_masking(ds, connectivity=1, dilation=None, land_dilation=10):
253260
return ocean_mask
254261

255262

256-
def coastal_masking(ds, buffer=50, closing=None):
263+
def coastal_masking(ds, ocean_land_da, buffer=50, closing=None):
257264
"""
258265
Creates a symmetrical buffer around the land-water boundary
259266
in a input boolean array. This is used to create a study area
260267
mask that is focused on the coastal zone, excluding inland or
261-
deeper ocean pixels. This region can be optionally dilated to
262-
ensure that the sub-pixel algorithm has pixels on either side
263-
of the water index threshold.
268+
deeper ocean pixels.
264269
265270
Parameters:
266271
-----------
267272
ds : xarray.DataArray
268273
A single time-step boolean array containing True for land
269274
pixels, and False for water.
275+
ocean_land_da : xarray.DataArray
276+
A supplementary dataset used to separate ocean waters from
277+
other inland water. The array should contain values of 0
278+
for ocean, and values of 1 or greater for land pixels.
279+
For Australia, we use the Geodata 100K coastline dataset,
280+
rasterized as the "geodata_coast_100k" product on the
281+
DEA datacube.
270282
buffer : integer, optional
271283
The number of pixels to buffer the land-water boundary in
272284
each direction.
@@ -289,8 +301,9 @@ def _coastal_buffer(ds, buffer):
289301
if closing:
290302
ds = xr.apply_ufunc(binary_closing, ds, disk(closing))
291303

292-
# Identify ocean pixels that are directly connected to tide points
293-
all_time_ocean = ocean_masking(ds)
304+
# Identify ocean pixels based on overlap with the Geodata
305+
# 100K coastline dataset
306+
all_time_ocean = ocean_masking(ds, ocean_land_da)
294307

295308
# Generate coastal buffer from ocean-land boundary
296309
coastal_mask = xr.apply_ufunc(
@@ -611,14 +624,28 @@ def contours_preprocess(
611624
# Set any pixels outside mask to 0 to represent water
612625
thresholded_ds = thresholded_ds.where(temporal_mask, 0)
613626

614-
# Identify pixels that are land in at least 15% of valid observations,
615-
# and use this to generate a coastal buffer study area. Morphological
616-
# closing helps to "close" the entrances of estuaries and rivers, removing
617-
# them from the analysis
627+
# Identify pixels that are land in at least 15% of valid observations;
628+
# this is used to generate an all-of-time buffered coastal study area
629+
# that constrains the Coastlines analysis to the coastal zone
618630
all_time = thresholded_ds.mean(dim="year") >= 0.15
619631
all_time = all_time.odc.assign_crs(crs=yearly_ds.odc.crs)
620632

621-
coastal_mask = coastal_masking(ds=all_time, buffer=buffer_pixels, closing=15)
633+
# Load Geodata 100K coastal layer to use to separate ocean waters from
634+
# other inland waters. This product has values of 0 for ocean waters,
635+
# and values of 1 and 2 for mainland/island pixels.
636+
dc = datacube.Datacube()
637+
geodata_da = dc.load(
638+
product="geodata_coast_100k",
639+
like=all_time.odc.geobox.compat,
640+
dask_chunks={},
641+
).land.squeeze("time")
642+
643+
# Use all time and Geodata 100K data to produce the buffered coastal
644+
# study area. Morphological closing helps to "close" the entrances of
645+
# estuaries and rivers, removing them from the analysis.
646+
coastal_mask = coastal_masking(
647+
ds=all_time, ocean_land_da=geodata_da, buffer=buffer_pixels, closing=15
648+
)
622649

623650
# Optionally modify the coastal mask using manually supplied polygons to
624651
# add missing areas of shoreline, or remove unwanted areas from the mask.
@@ -653,7 +680,7 @@ def contours_preprocess(
653680
(thresholded_ds != 0) # Set both 1s and NaN to True
654681
.where(~inland_mask, 1)
655682
.groupby("year")
656-
.apply(lambda x: ocean_masking(x, 1, 3))
683+
.apply(lambda x: ocean_masking(x, geodata_da, 1, 3))
657684
)
658685

659686
# Keep pixels within annual mask layers, all time coastal buffer,

0 commit comments

Comments
 (0)