From bab73d73532a07057bb7f03e2604b54b62a3c465 Mon Sep 17 00:00:00 2001 From: Paul Hobson Date: Tue, 10 Jul 2018 19:00:37 -0700 Subject: [PATCH 1/5] keep track of cell masks when grids are merged --- pygridtools/core.py | 44 ++++++++------- pygridtools/misc.py | 5 ++ .../test_ModelGrid_merge_with_mask.png | Bin 0 -> 14973 bytes pygridtools/tests/test_core.py | 52 +++++++++++++++++- pygridtools/tests/test_misc.py | 29 ++++++++++ 5 files changed, 110 insertions(+), 20 deletions(-) create mode 100644 pygridtools/tests/baseline_files/test_core/test_ModelGrid_merge_with_mask.png diff --git a/pygridtools/core.py b/pygridtools/core.py index a89df59..b2bea9c 100644 --- a/pygridtools/core.py +++ b/pygridtools/core.py @@ -325,15 +325,17 @@ def cell_mask(self): def cell_mask(self, value): self._cell_mask = value + @property + def node_mask(self): + padded = numpy.pad(self.cell_mask, pad_width=1, mode='edge') + windowed = misc.padded_sum(padded.astype(int), window=1) + return (windowed == 4) + @property def crs(self): """ Coordinate reference system for GIS data export """ return self._crs - @crs.setter - def crs(self, value): - self._crs = value - @property def domain(self): """ The optional domain used to generate the raw grid """ @@ -543,9 +545,10 @@ def extract(self, jstart=0, istart=0, jend=-1, iend=-1): return self.transform(extract, jstart=jstart, istart=istart, jend=jend, iend=iend) def copy(self): + """ Returns a deep copy of the current ModelGrid """ return deepcopy(self) - def merge(self, other, how='vert', where='+', shift=0): + def merge(self, other, how='vert', where='+', shift=0, min_nodes=1): """ Merge with another grid using pygridtools.misc.padded_stack. @@ -581,6 +584,8 @@ def merge(self, other, how='vert', where='+', shift=0): axis other than the one being merged. In other words, vertically stacked arrays can be shifted horizontally, and horizontally stacked arrays can be shifted vertically. + min_nodes : int (default = 1) + Minimum number of masked nodes required to mask a cell. Returns ------- @@ -620,10 +625,13 @@ def merge(self, other, how='vert', where='+', shift=0): """ + node_mask = merge(self.node_mask, other.node_mask, how=how, where=where, shift=shift) + cell_mask = misc.padded_sum(node_mask) >= min_nodes + return ModelGrid( merge(self.nodes_x, other.nodes_x, how=how, where=where, shift=shift), merge(self.nodes_y, other.nodes_y, how=how, where=where, shift=shift) - ).update_cell_mask() + ).update_cell_mask(mask=cell_mask) def update_cell_mask(self, mask=None, merge_existing=True): """ @@ -694,16 +702,12 @@ def mask_nodes(self, polyverts, min_nodes=3, inside=False, use_existing=False, _node_mask = misc.mask_with_polygon(self.xn, self.yn, polyverts, inside=inside).astype(int) - cell_mask = ( - _node_mask[1:, 1:] + _node_mask[:-1, :-1] + - _node_mask[:-1, 1:] + _node_mask[1:, :-1] - ) >= min_nodes - cell_mask = cell_mask.astype(bool) + + cell_mask = (misc.padded_sum(_node_mask, window=1) >= min_nodes).astype(bool) return self.update_cell_mask(mask=cell_mask, merge_existing=use_existing) def mask_centroids(self, polyverts, inside=True, use_existing=True): - """ Create mask for the cells of the ModelGrid with a polygon. Parameters @@ -736,10 +740,9 @@ def mask_cells_with_polygon(self, polyverts, use_centroids=True, **kwargs): else: return self.mask_nodes(polyverts, **kwargs) - def plot_cells(self, engine='mpl', ax=None, - usemask=True, cell_kws=None, - domain_kws=None, extent_kws=None, - showisland=True, island_kws=None): + def plot_cells(self, engine='mpl', ax=None, usemask=True, showisland=True, + cell_kws=None, domain_kws=None, extent_kws=None, + island_kws=None): """ Creates a figure of the cells, boundary, domain, and islands. @@ -773,13 +776,16 @@ def plot_cells(self, engine='mpl', ax=None, mask=self.cell_mask, **cell_kws) if domain_kws is not None: - fig = viz.plot_domain(data=self.domain, engine=engine, ax=ax, **domain_kws) + fig = viz.plot_domain(data=self.domain, engine=engine, + ax=ax, **domain_kws) if extent_kws: - fig = viz.plot_boundaries(extent=self.extent, engine=engine, ax=ax, **extent_kws) + fig = viz.plot_boundaries(extent=self.extent, engine=engine, + ax=ax, **extent_kws) if island_kws: - fig = viz.plot_boundaries(islands=self.islands, engine=engine, ax=ax, **island_kws) + fig = viz.plot_boundaries(islands=self.islands, engine=engine, + ax=ax, **island_kws) return fig diff --git a/pygridtools/misc.py b/pygridtools/misc.py index a4f9e53..5ddf050 100644 --- a/pygridtools/misc.py +++ b/pygridtools/misc.py @@ -273,6 +273,11 @@ def padded_stack(a, b, how='vert', where='+', shift=0, padval=numpy.nan): return stacked +def padded_sum(padded, window=1): + return (padded[window:, window:] + padded[:-window, :-window] + + padded[:-window, window:] + padded[window:, :-window]) + + def mask_with_polygon(x, y, polyverts, inside=True): """ Mask x-y arrays inside or outside a polygon diff --git a/pygridtools/tests/baseline_files/test_core/test_ModelGrid_merge_with_mask.png b/pygridtools/tests/baseline_files/test_core/test_ModelGrid_merge_with_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..05c0e39344495735e7c7241e08c5f1ea13d1315e GIT binary patch literal 14973 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 Date: Tue, 10 Jul 2018 19:05:25 -0700 Subject: [PATCH 2/5] use strict testing in travis directly --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3a08d28..d270106 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,9 +27,8 @@ install: - pip install codecov - pip install . - script: - - coverage run --source pygridtools check_pygridtools.py --verbose --pep8 --mpl + - coverage run --source pygridtools check_pygridtools.py --verbose --strict after_success: - if [ ${COVERAGE} = true ]; then From 2854a8634e31028ad0314a1c0217a3e254a57ad0 Mon Sep 17 00:00:00 2001 From: Paul Hobson Date: Tue, 10 Jul 2018 19:05:58 -0700 Subject: [PATCH 3/5] use padded_sum function in other places --- pygridtools/core.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/pygridtools/core.py b/pygridtools/core.py index b2bea9c..0519941 100644 --- a/pygridtools/core.py +++ b/pygridtools/core.py @@ -251,20 +251,12 @@ def nodes_y(self, value): @property def cells_x(self): """Array of cell centroid x-coordinates""" - xc = 0.25 * ( - self.xn[1:, 1:] + self.xn[1:, :-1] + - self.xn[:-1, 1:] + self.xn[:-1, :-1] - ) - return xc + return 0.25 * misc.padded_sum(self.xn) @property def cells_y(self): """Array of cell centroid y-coordinates""" - yc = 0.25 * ( - self.yn[1:, 1:] + self.yn[1:, :-1] + - self.yn[:-1, 1:] + self.yn[:-1, :-1] - ) - return yc + return 0.25 * misc.padded_sum(self.yn) @property def shape(self): From 7e9a64539aaf2063c972079011ea8b28d1a3247e Mon Sep 17 00:00:00 2001 From: Paul Hobson Date: Tue, 10 Jul 2018 19:07:02 -0700 Subject: [PATCH 4/5] clean up docstring whitespace --- pygridtools/core.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pygridtools/core.py b/pygridtools/core.py index 0519941..a8fefcb 100644 --- a/pygridtools/core.py +++ b/pygridtools/core.py @@ -270,42 +270,42 @@ def cell_shape(self): @property def xn(self): - """Shortcut to x-coords of nodes""" + """ Shortcut to x-coords of nodes """ return self.nodes_x @property def yn(self): - """Shortcut to y-coords of nodes""" + """ Shortcut to y-coords of nodes """ return self.nodes_y @property def xc(self): - """ Shortcut to x-coords of cells/centroids""" + """ Shortcut to x-coords of cells/centroids """ return self.cells_x @property def yc(self): - """ Shortcut to y-coords of cells/centroids""" + """ Shortcut to y-coords of cells/centroids """ return self.cells_y @property def icells(self): - """ Number of rows of cells""" + """ Number of rows of cells """ return self.cell_shape[1] @property def jcells(self): - """ Number of columns of cells""" + """ Number of columns of cells """ return self.cell_shape[0] @property def inodes(self): - """Number of rows of nodes""" + """Number of rows of nodes """ return self.shape[1] @property def jnodes(self): - """Number of columns of nodes""" + """Number of columns of nodes """ return self.shape[0] @property From 388c09132dbd70413d13d82524b90bc239adb1e9 Mon Sep 17 00:00:00 2001 From: Paul Hobson Date: Tue, 10 Jul 2018 19:07:28 -0700 Subject: [PATCH 5/5] WIP: stubbed out reprojection and geodataframe integration --- pygridtools/core.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pygridtools/core.py b/pygridtools/core.py index a8fefcb..196c56c 100644 --- a/pygridtools/core.py +++ b/pygridtools/core.py @@ -214,6 +214,9 @@ class ModelGrid(object): ---------- nodes_x, nodes_y : numpy.ndarray M-by-N arrays of node (vertex) coordinates for the grid. + crs : string + proj4-compliant coordinate reference system specification. See + http://geopandas.org/projections.html for more information. """ def __init__(self, nodes_x, nodes_y, crs=None): @@ -836,6 +839,9 @@ def make_cols(top_level): northing = pandas.DataFrame(y, index=index, columns=northing_cols) return easting.join(northing) + def to_geodataframe(self, usemask=True, which='nodes'): + pass + def to_coord_pairs(self, usemask=False, which='nodes'): """ Converts a grid to a long array of coordinates pairs. @@ -930,6 +936,13 @@ def to_gis(self, outputfile, usemask=True, which='cells', def to_gefdc(self, directory): return GEFDCWriter(self, directory) + def reproject(self, crs): + return ( + self.to_geodataframe(usemask=True, which='nodes') + .to_crs(crs) + .pipe(ModelGrid.from_geodataframe) + ) + @classmethod def from_dataframe(cls, df, icol='ii', jcol='jj', xcol='easting', ycol='northing'): @@ -955,6 +968,10 @@ def from_dataframe(cls, df, icol='ii', jcol='jj', xtab = df.reset_index()[all_cols].set_index([icol, jcol]).unstack(level=icol) return cls(xtab[xcol], xtab[ycol]).update_cell_mask() + @classmethod + def from_geodataframe(cls, gdf, icol='ii', jcol='jj'): + pass + @classmethod def from_gis(cls, gisfile, icol='ii', jcol='jj'): """