Skip to content

Commit 194372c

Browse files
committed
Merge branch 'master' of github.com:csiro-hydroinformatics/hydrodiy
2 parents 56b0f94 + 21f4573 commit 194372c

File tree

1 file changed

+65
-24
lines changed

1 file changed

+65
-24
lines changed

hydrodiy/gis/grid.py

+65-24
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
import numpy as np
1818
import pandas as pd
1919
from scipy.interpolate import griddata
20-
from scipy.ndimage import gaussian_filter, maximum_filter
20+
from scipy.ndimage import gaussian_filter, maximum_filter, \
21+
binary_fill_holes
2122

2223
from hydrodiy.gis import gutils
2324
from hydrodiy.io import csv
@@ -1019,6 +1020,7 @@ def __init__(self, name, flowdir):
10191020
self._idxcell_outlet = None
10201021
self._idxinlets = None
10211022
self._idxcells_area = None
1023+
self._idxcells_area_filled = None
10221024
self._idxcells_boundary = None
10231025
self._flowpathlengths = None
10241026
self._xycells_boundary = None
@@ -1075,8 +1077,10 @@ def __sub__(self, other):
10751077
catchment._idxcells_boundary = None
10761078
catchment._xycells_boundary = None
10771079

1078-
catchment._idxcells_area = np.setdiff1d(self._idxcells_area,
1079-
other._idxcells_area)
1080+
# Difference between filled cell boundary
1081+
catchment._idxcells_area = np.setdiff1d(\
1082+
self._idxcells_area_filled,
1083+
other._idxcells_area_filled)
10801084

10811085
return catchment
10821086

@@ -1103,6 +1107,9 @@ def from_dict(cls, dic):
11031107
catchment._idxcells_area = \
11041108
np.array(dic["idxcells_area"]).astype(np.int64)
11051109

1110+
catchment._idxcells_area_filled = \
1111+
np.array(dic["idxcells_area_filled"]).astype(np.int64)
1112+
11061113
return catchment
11071114

11081115

@@ -1122,6 +1129,12 @@ def idxcells_area(self):
11221129
""" Get cells of catchment area """
11231130
return self._idxcells_area
11241131

1132+
@property
1133+
def idxcells_area_filled(self):
1134+
""" Get cells of filled catchment area (no holes)"""
1135+
return self._idxcells_area_filled
1136+
1137+
11251138
@property
11261139
def flowpathlengths(self):
11271140
""" Get flowpaths cells expressed in number of cells """
@@ -1153,11 +1166,11 @@ def clone(self):
11531166
def extent(self):
11541167
""" Get catchment area extent """
11551168

1156-
if self._idxcells_area is None:
1157-
raise ValueError("idxcells_area is None, please" + \
1169+
if self._idxcells_area_filled is None:
1170+
raise ValueError("idxcells_area_filled is None, please" + \
11581171
" delineate the area")
11591172

1160-
xy = self._flowdir.cell2coord(self._idxcells_area)
1173+
xy = self._flowdir.cell2coord(self._idxcells_area_filled)
11611174
cz = self.flowdir.cellsize
11621175
return np.min(xy[:, 0])-cz/2, np.max(xy[:, 0])+cz/2, \
11631176
np.min(xy[:, 1])-cz/2, np.max(xy[:, 1])+cz/2,
@@ -1223,11 +1236,14 @@ def delineate_area(self, idxcell_outlet, idxinlets=None, nval=1000000):
12231236
buffer2 = -1*np.ones(nval, dtype=np.int64)
12241237

12251238
# Compute area
1239+
flowdir = self.flowdir
12261240
ierr = c_hydrodiy_gis.delineate_area(FLOWDIRCODE,
1227-
self.flowdir.data, self.idxcell_outlet, idxinlets,
1241+
flowdir.data, self.idxcell_outlet, idxinlets,
12281242
idxcells, buffer1, buffer2)
12291243

12301244
if ierr>0:
1245+
self._idxcells_area = None
1246+
self._idxcells_area_filled = None
12311247
raise ValueError(("c_hydrodiy_gis.delineate_area" + \
12321248
" returns {0}. Consider increasing " + \
12331249
"buffer size ({1})").format(ierr, nval))
@@ -1236,6 +1252,29 @@ def delineate_area(self, idxcell_outlet, idxinlets=None, nval=1000000):
12361252
self._idxcells_area = idxcells[idx]
12371253

12381254

1255+
if idx.sum()>0:
1256+
# Fill area cells
1257+
# .. create a minimal rectangle containing catchment area
1258+
rowcol = flowdir.cell2rowcol(self._idxcells_area)
1259+
i0, j0 = np.maximum(0, rowcol.min(axis=0)-1)
1260+
i1, j1 = rowcol.max(axis=0)+1
1261+
i1 = min(flowdir.nrows-1, i1)
1262+
j1 = min(flowdir.ncols-1, j1)
1263+
nrows, ncols = i1-i0+1, j1-j0+1
1264+
grid = np.zeros((nrows, ncols), dtype=int)
1265+
grid[rowcol[:, 0]-i0, rowcol[:, 1]-j0] = 1
1266+
# .. fill holes
1267+
grid = binary_fill_holes(grid)
1268+
# .. get cell coordinates
1269+
irows, icols = np.where(grid==1)
1270+
irows += i0
1271+
icols += j0
1272+
filled = irows*flowdir.ncols+icols
1273+
self._idxcells_area_filled = filled
1274+
else:
1275+
self._idxcells_area_filled = self._idxcells_area
1276+
1277+
12391278
def delineate_boundary(self, catchment_area_mask=None):
12401279
""" Delineate catchment boundary from area """
12411280
has_c_module("gis")
@@ -1248,20 +1287,22 @@ def delineate_boundary(self, catchment_area_mask=None):
12481287
ncols = self._flowdir.ncols
12491288

12501289
# Initialise boundary cells with vector of same length
1251-
# than area
1252-
nval = np.int64(len(self._idxcells_area))
1290+
# than area. Use FILLED area cells!
1291+
cells_area = self._idxcells_area_filled
1292+
nval = np.int64(len(cells_area))
12531293
idxcells_boundary = -1*np.ones(nval, dtype=np.int64)
12541294
buf = -1*np.ones(nval, dtype=np.int64)
12551295

12561296
# Initialise catchment area mask
12571297
if catchment_area_mask is None:
12581298
catchment_area_mask = np.zeros(nrows*ncols, dtype=np.int64)
1259-
catchment_area_mask[self._idxcells_area] = 1
1299+
catchment_area_mask[cells_area] = 1
12601300

1301+
# Use filled area (algorithm failed when there are holes)
12611302
# Compute boundary with varying dmax
12621303
# This point could be improved!!
12631304
ierr = c_hydrodiy_gis.delineate_boundary(nrows, ncols, \
1264-
self._idxcells_area, \
1305+
cells_area, \
12651306
buf, catchment_area_mask, \
12661307
idxcells_boundary)
12671308

@@ -1332,7 +1373,7 @@ def compute_flowpathlengths(self):
13321373
"idxcell_end", "length[cell]"])
13331374

13341375

1335-
def intersect(self, grid):
1376+
def intersect(self, grid, filled=False):
13361377
""" Intersect catchment area with other grid and compute
13371378
the weight of each cell from the new grid falling into the
13381379
catchment.
@@ -1341,6 +1382,8 @@ def intersect(self, grid):
13411382
-----------
13421383
grid : hydrodiy.grid.Grid
13431384
Input grid (a.g. rainfall data grid).
1385+
filled : bool
1386+
Used filled catchment area.
13441387
13451388
Returns
13461389
-----------
@@ -1375,7 +1418,8 @@ def intersect(self, grid):
13751418
xll, yll, csz, nrows, ncols = grid._getsize()
13761419
_, _, csz_area, _, _ = self.flowdir._getsize()
13771420

1378-
xy_area = self.flowdir.cell2coord(self._idxcells_area)
1421+
cells = self._idxcells_area_filled if filled else self._idxcells_area
1422+
xy_area = self.flowdir.cell2coord(cells)
13791423
npoints = np.zeros((1,), dtype=np.int64)
13801424
idxcells = np.zeros(nrows*ncols, dtype=np.int64)
13811425
weights = np.zeros(nrows*ncols, dtype=np.float64)
@@ -1430,14 +1474,15 @@ def intersect(self, grid):
14301474
return area_grid, idxcells, weights
14311475

14321476

1433-
def isin(self, idxcell):
1477+
def isin(self, idxcell, filled=False):
14341478
""" Check if a cell is within the catchment area """
14351479

14361480
if self._idxcells_area is None:
14371481
raise ValueError("idxcells_area is None, " + \
14381482
"please delineate the area")
14391483

1440-
return idxcell in self._idxcells_area
1484+
cells = self._idxcells_area_filled if filled else self._idxcells_area
1485+
return idxcell in cells
14411486

14421487

14431488
def compute_area(self, to_proj, from_proj=None):
@@ -1495,7 +1540,10 @@ def plot_area(self, ax, *args, **kwargs):
14951540
raise ValueError("idxcells_boundary is None, " + \
14961541
"please delineate the area")
14971542

1498-
xy = self.flowdir.cell2coord(self._idxcells_area)
1543+
filled = kwargs.get("filled", False)
1544+
cells = self._idxcells_area_filled if filled else self._idxcells_area
1545+
1546+
xy = self.flowdir.cell2coord(cells)
14991547
ax.plot(xy[:, 0], xy[:, 1], *args, **kwargs)
15001548

15011549

@@ -1526,19 +1574,12 @@ def to_dict(self):
15261574
"idxcell_outlet":self._idxcell_outlet,
15271575
"idxinlets":inlets,
15281576
"idxcells_area":list(self._idxcells_area),
1577+
"idxcells_area_filled":list(self._idxcells_area_filled),
15291578
"flowdir":self.flowdir.to_dict(),
15301579
}
15311580
return dic
15321581

15331582

1534-
def load(self, filename):
1535-
""" Load data from a JSON file """
1536-
1537-
if not filename.endswith("json"):
1538-
raise ValueError(("Filename ({0}) should end with a" + \
1539-
" bil extension").format(filename))
1540-
1541-
15421583

15431584
def delineate_river(flowdir, idxupstream, nval=1000000):
15441585
""" Delineate river upstream point and flow direction grid

0 commit comments

Comments
 (0)