@@ -190,24 +190,38 @@ def load_rasters(
190
190
# return ocean_mask
191
191
192
192
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 ):
194
194
"""
195
195
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.
197
199
198
200
Parameters:
199
201
-----------
200
202
ds : xarray.DataArray
201
203
An array containing True for land pixels, and False for water.
202
204
This can be obtained by thresholding a water index
203
205
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.
204
213
connectivity : integer, optional
205
214
An integer passed to the 'connectivity' parameter of the
206
215
`skimage.measure.label` function.
207
- dilation : integer, optional
216
+ dilation : integer, optional
208
217
The number of pixels to dilate ocean pixels to ensure than
209
218
adequate land pixels are included for subpixel waterline
210
219
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.
211
225
212
226
Returns:
213
227
--------
@@ -216,15 +230,8 @@ def ocean_masking(ds, connectivity=1, dilation=None, land_dilation=10):
216
230
pixels as True.
217
231
"""
218
232
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
-
226
233
# 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
228
235
229
236
# Dilate to restrict to deeper ocean, and invert to get water
230
237
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):
253
260
return ocean_mask
254
261
255
262
256
- def coastal_masking (ds , buffer = 50 , closing = None ):
263
+ def coastal_masking (ds , ocean_land_da , buffer = 50 , closing = None ):
257
264
"""
258
265
Creates a symmetrical buffer around the land-water boundary
259
266
in a input boolean array. This is used to create a study area
260
267
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.
264
269
265
270
Parameters:
266
271
-----------
267
272
ds : xarray.DataArray
268
273
A single time-step boolean array containing True for land
269
274
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.
270
282
buffer : integer, optional
271
283
The number of pixels to buffer the land-water boundary in
272
284
each direction.
@@ -289,8 +301,9 @@ def _coastal_buffer(ds, buffer):
289
301
if closing :
290
302
ds = xr .apply_ufunc (binary_closing , ds , disk (closing ))
291
303
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 )
294
307
295
308
# Generate coastal buffer from ocean-land boundary
296
309
coastal_mask = xr .apply_ufunc (
@@ -611,14 +624,28 @@ def contours_preprocess(
611
624
# Set any pixels outside mask to 0 to represent water
612
625
thresholded_ds = thresholded_ds .where (temporal_mask , 0 )
613
626
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
618
630
all_time = thresholded_ds .mean (dim = "year" ) >= 0.15
619
631
all_time = all_time .odc .assign_crs (crs = yearly_ds .odc .crs )
620
632
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
+ )
622
649
623
650
# Optionally modify the coastal mask using manually supplied polygons to
624
651
# add missing areas of shoreline, or remove unwanted areas from the mask.
@@ -653,7 +680,7 @@ def contours_preprocess(
653
680
(thresholded_ds != 0 ) # Set both 1s and NaN to True
654
681
.where (~ inland_mask , 1 )
655
682
.groupby ("year" )
656
- .apply (lambda x : ocean_masking (x , 1 , 3 ))
683
+ .apply (lambda x : ocean_masking (x , geodata_da , 1 , 3 ))
657
684
)
658
685
659
686
# Keep pixels within annual mask layers, all time coastal buffer,
0 commit comments