Commit 9f0eefc8 authored by Daniel Scheffler's avatar Daniel Scheffler
Browse files

Added new methods to show X/Y/Z profiles of the GeoArray instance


  (GeoArray.show_xprofile(), GeoArray.show_yprofile() and GeoArray.show_zprofile()) + tests and documentation. Bumped version.
Signed-off-by: Daniel Scheffler's avatarDaniel Scheffler <danschef@gfz-potsdam.de>
parent 3fbe77ec
Pipeline #25444 passed with stages
in 2 minutes and 14 seconds
......@@ -2,6 +2,13 @@
History
=======
0.13.0 (2021-07-08)
-------------------
* Added new methods to show X/Y/Z profiles of the GeoArray instance
(GeoArray.show_xprofile(), GeoArray.show_yprofile() and GeoArray.show_zprofile()) + tests and documentation.
0.12.5 (2021-07-08)
-------------------
......
This diff is collapsed.
......@@ -1446,6 +1446,104 @@ class GeoArray(object):
print('2 % percentile:', np.nanpercentile(data, 2))
print('98 % percentile:', np.nanpercentile(data, 98))
def _show_profile(self, x, y, xlabel, ylabel, title, xlim, ylim, figsize, show_nodata, return_fig):
from matplotlib import pyplot as plt
nd = None
if self._nodata is not None and self._nodata in y:
if show_nodata:
nd = np.ma.masked_not_equal(y, self._nodata)
title += ' (no-data is indicated in red)'
else:
title += ' (no-data is not shown)'
y = np.ma.masked_equal(y, self._nodata)
fig = plt.figure(figsize=figsize)
plt.plot(x, y, 'k')
if show_nodata and nd is not None:
plt.plot(x, nd, 'r')
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.xlim(*xlim or (min(x), max(x)))
if ylim:
plt.ylim(*ylim)
plt.grid()
plt.title(title)
plt.show()
if return_fig:
return fig
else:
plt.show()
def show_xprofile(self, row, band, xlim=None, ylim=None, figsize=(10, 5), show_nodata=True, return_fig=False):
"""Show an x-profile at the given row/band image position.
:param row: image row number (counts from 0)
:param band: image band number (counts from 0)
:param xlim: x-axis limits to be used in the plot
:param ylim: y-axis limits to be used in the plot
:param figsize: figure size (tuple)
:param show_nodata: whether to show no-data values in the plot
:param return_fig: whether to return the figure instead of showing it directly
:return: plt.figure
"""
x = range(self.columns)
y = self[row, :, band]
title = f'X-Profile at row {range(self.rows)[row]}, band {range(self.bands)[band]}'
return self._show_profile(x, y, 'column', 'value', title, xlim, ylim, figsize, show_nodata, return_fig)
def show_yprofile(self, column, band, xlim=None, ylim=None, figsize=(10, 5), show_nodata=True, return_fig=False):
"""Show a y-profile at the given column/band image position.
:param column: image column number (counts from 0)
:param band: image band number (counts from 0)
:param xlim: x-axis limits to be used in the plot
:param ylim: y-axis limits to be used in the plot
:param figsize: figure size (tuple)
:param show_nodata: whether to show no-data values in the plot
:param return_fig: whether to return the figure instead of showing it directly
:return: plt.figure
"""
x = range(self.rows)
y = self[:, column, band]
title = f'Y-Profile at column {range(self.columns)[column]}, band {range(self.bands)[band]}'
return self._show_profile(x, y, 'row', 'value', title, xlim, ylim, figsize, show_nodata, return_fig)
def show_zprofile(self, row, column, xlim=None, ylim=None, figsize=(10, 5), show_nodata=True, return_fig=False):
"""Show a z-profile at the given row/column image position.
:param row: image row number (counts from 0)
:param column: image column number (counts from 0)
:param xlim: x-axis limits to be used in the plot
:param ylim: y-axis limits to be used in the plot
:param figsize: figure size (tuple)
:param show_nodata: whether to show no-data values in the plot
:param return_fig: whether to return the figure instead of showing it directly
:return: plt.figure
"""
if self.ndim <= 2:
raise RuntimeError(f'Plotting a z-profile is not possible for a {self.ndim}D array.')
if 'wavelength' in self.meta.band_meta:
x = self.meta.band_meta['wavelength']
x_label = 'wavelength'
else:
x = range(self.bands)
x_label = 'band'
y = self[row, column, :]
title = f'Z-Profile at row {range(self.rows)[row]}, column {range(self.columns)[column]}'
return self._show_profile(x, y, x_label, 'value', title, xlim, ylim, figsize, show_nodata, return_fig)
def clip_to_footprint(self):
"""Clip the GeoArray instance to the outer bounds of the actual footprint."""
self.clip_to_poly(self.footprint_poly)
......
......@@ -20,5 +20,5 @@
# with this program. If not, see <http://www.gnu.org/licenses/>.
__version__ = '0.12.5'
__versionalias__ = '20210708.01'
__version__ = '0.13.0'
__versionalias__ = '20210708.02'
......@@ -510,6 +510,30 @@ class Test_GeoArray(TestCase):
def test_show_histogram(self):
self.gA.show_histogram()
def test_show_xprofile(self):
nd_old = self.gA._nodata
self.gA._nodata = self.gA[0, 0, 0]
self.gA.show_xprofile(0, 0)
self.gA.show_xprofile(0, 0, show_nodata=False)
self.gA._nodata = nd_old
def test_show_yprofile(self):
self.gA.show_yprofile(0, 0)
self.gA.show_yprofile(0, 0, show_nodata=False)
def test_show_zprofile(self):
self.gA.show_zprofile(0, 0)
self.gA.show_zprofile(0, 0, show_nodata=False)
self.gA.show_zprofile(0, 0, ylim=(0, 5))
with self.assertRaises(RuntimeError):
GeoArray(np.random.randint(1, 10, [10, 10])).show_zprofile(0, 0)
if 'wavelength' not in self.gA.meta.band_meta:
self.gA.meta.band_meta['wavelength'] = range(self.gA.bands)
self.gA.show_zprofile(0, 0, ylim=(0, 5))
del self.gA.meta.band_meta['wavelength']
def test_get_mapPos(self):
# TODO: validate results (pixel dimensions, values, metadata subset, ...)
xmin, xmax, ymin, ymax = self.gA.box.boundsMap
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment