-
Notifications
You must be signed in to change notification settings - Fork 219
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow passing RGB xarray.DataArray images into grdimage #2590
Conversation
Saving 3-band xarray.DataArray images to a temporary GeoTIFF so that they can be plotted with grdimage.
Summary of changed imagesThis is an auto-generated report of images that have changed on the DVC remote
Image diff(s)Report last updated at commit 15a7207 |
geotiff = which(fname="@earth_day_01d_p", download="c") | ||
with rioxarray.open_rasterio(filename=geotiff) as rda: | ||
if len(rda.band) == 1: | ||
with rasterio.open(fp=geotiff) as src: | ||
df_colormap = pd.DataFrame.from_dict( | ||
data=src.colormap(1), orient="index" | ||
) | ||
array = src.read() | ||
|
||
red = np.vectorize(df_colormap[0].get)(array) | ||
green = np.vectorize(df_colormap[1].get)(array) | ||
blue = np.vectorize(df_colormap[2].get)(array) | ||
# alpha = np.vectorize(df_colormap[3].get)(array) | ||
|
||
rda.data = red | ||
da_red = rda.astype(dtype=np.uint8).copy() | ||
rda.data = green | ||
da_green = rda.astype(dtype=np.uint8).copy() | ||
rda.data = blue | ||
da_blue = rda.astype(dtype=np.uint8).copy() | ||
|
||
xr_image = xr.concat(objs=[da_red, da_green, da_blue], dim="band") | ||
assert xr_image.sizes == {"band": 3, "y": 180, "x": 360} | ||
return xr_image |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should replace this with the load_blue_marble
function from #2235 once done. Ideally, the rioxarray.open_rasterio
function would be able to load this 3-band GeoTIFF from colorinterp directly without the hacky logic here.
pygmt/src/grdimage.py
Outdated
with GMTTempFile(suffix=".tif") as tmpfile: | ||
if hasattr(grid, "dims") and len(grid.dims) == 3: | ||
grid.rio.to_raster(raster_path=tmpfile.name) | ||
_grid = tmpfile.name | ||
else: | ||
_grid = grid |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should maybe put this logic in lib.virtualfile_from_data
, since we'll need to reuse it for grdview
, grdcut
, and possibly other GMT modules that work with GMT_IMAGE.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, created a new tempfile_from_image
function at 89da916 (similar to tempfile_from_geojson
). This can be replaced with lib.virtualfile_from_image
once that is implemented.
Putting the temporary GeoTIFF creation logic in a dedicated tempfile_from_image helper function, so that it can be reused by other GMT modules besides grdimage. Also ensure that an ImportError is raised when the `.rio` attribute cannot be found when rioxarray is not installed.
Refactor Figure.tilemap to use the same tempfile_from_image function that generates a temporary GeoTIFF file from the 3-band xarray.DataArray images.
Various updates from upstream GMT at GenericMappingTools/gmt#6258, GenericMappingTools/gmt@9069967, GenericMappingTools/gmt#7260.
Plotting a non-uint8 dtype xarray.DataArray works in grdimage, but the results will likely be incorrect. Warning the user about the incorrect dtype, and suggest recasting to uint8 with 0-255 range, e.g. using a histogram equalization function like skimage.exposure.equalize_hist.
if kind == "image" and data.dtype != "uint8": | ||
msg = ( | ||
f"Input image has dtype: {data.dtype} which is unsupported, " | ||
"and may result in an incorrect output. Please recast image " | ||
"to a uint8 dtype and/or scale to 0-255 range, e.g. " | ||
"using a histogram equalization function like " | ||
"skimage.exposure.equalize_hist." | ||
) | ||
warnings.warn(message=msg, category=RuntimeWarning, stacklevel=2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Took a look at grdhisteq
, but it doesn't appear to support 3-band image inputs, only 1-band grids, so suggesting to use skimage.exposure.equalize_hist
instead. Could probably raise a feature request to upstream GMT to let grdhisteq support this too?
pygmt/helpers/utils.py
Outdated
@@ -112,7 +112,7 @@ def data_kind(data=None, x=None, y=None, z=None, required_z=False): | |||
Returns | |||
------- | |||
kind : str | |||
One of: ``'file'``, ``'grid'``, ``'matrix'``, ``'vectors'``. | |||
One of: ``'file'``, ``'grid'``, ``image``, ``'matrix'``, ``'vectors'``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO handle merge conflicts after #2493 is merged.
Co-authored-by: Dongdong Tian <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR looks good to me. Are there any more changes you want to make into this PR?
Co-authored-by: Dongdong Tian <[email protected]>
Was planning to wait for #2493 (comment) to be finalized so I can merge those changes on top of here, but I can merge this PR first too. Wanted to add some extra tests for Edit: Hmm, and there seems to be a failing test on the Docs build at https://github.com/GenericMappingTools/pygmt/actions/runs/5615992349/job/15217416041?pr=2590#step:7:725 - |
On second thought, let's do the unit tests for |
Partially revert a773929 to fix `AttributeError: 'DataArray' object has no attribute 'rio'` in `pygmt/src/tilemap.py`, because the reproject from EPSG:3857 to OGC:CRS84 step requires it.
Description of proposed changes
Enables
grdimage
to plot 3-band RGB images stored in anxarray.DataArray
with a shape like (3, Y, X).Example usage based on https://corteva.github.io/rioxarray/stable/examples/COG.html:
produces
Implementation currently saves the 3-band
xarray.DataArray
image to a temporary GeoTIFF before passing it togrdimage
, similar to whattilemap
did in #2394pygmt/pygmt/src/tilemap.py
Lines 151 to 156 in e98efe1
Notes:
xarray.DataArray
to temporary GeoTIFF file requiresrioxarray
.grdhisteq
Wrap grdhisteq #1433 or normalization to 0-255 viagrdmix
(Wrap grdmix #1437) is appliedGMT_IMAGE
is wrapped in PyGMT so that temporary files can be avoided, see Integrate with rioxarray and implement GMT_IMAGE to read RGB GeoTIFFs #1555. The unit tests in this PR should still be valid though.TODO:
rioxarray
dependencygrdview
too?Closes #370, partially addresses #1555.
Reminders
make format
andmake check
to make sure the code follows the style guide.doc/api/index.rst
.Slash Commands
You can write slash commands (
/command
) in the first line of a comment to performspecific operations. Supported slash commands are:
/format
: automatically format and lint the code/test-gmt-dev
: run full tests on the latest GMT development version