-
Notifications
You must be signed in to change notification settings - Fork 235
Wrap GMT's standard data type GMT_IMAGE for images #3338
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
Changes from 12 commits
921674c
c01d6ab
36430c8
a918cfd
1983b7e
84cbd11
f887a23
95275b0
d43f2e9
47d4e4a
bb0efe3
994de3a
8d9c2ae
75c0f7e
dc1a649
0638dab
7080d8b
8692161
aeb2ea3
dad88ce
3f2f6bf
74b889f
bf7f0b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| """ | ||
| Wrapper for the GMT_IMAGE data type. | ||
| """ | ||
|
|
||
| import ctypes as ctp | ||
| from typing import ClassVar | ||
|
|
||
| from pygmt.datatypes.grid import _GMT_GRID_HEADER | ||
|
|
||
|
|
||
| class _GMT_IMAGE(ctp.Structure): # noqa: N801 | ||
| """ | ||
| GMT image data structure. | ||
|
|
||
| Examples | ||
| -------- | ||
| >>> import numpy as np | ||
| >>> from pygmt.clib import Session | ||
| >>> with Session() as lib: | ||
| ... with lib.virtualfile_out(kind="image") as voutimg: | ||
| ... lib.call_module("read", ["@earth_day_01d", voutimg, "-Ti"]) | ||
| ... # Read the image from the virtual file | ||
| ... image = lib.read_virtualfile(vfname=voutimg, kind="image").contents | ||
| ... # The image header | ||
| ... header = image.header.contents | ||
| ... # Access the header properties | ||
| ... print(header.n_rows, header.n_columns, header.registration) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. header.z_min and header.z_max are not checked here, because they have invalid values like 1.79769313486231570000e+308 (i.e., |
||
| ... print(header.wesn[:], header.inc[:]) | ||
| ... print(header.z_scale_factor, header.z_add_offset) | ||
| ... print(header.x_units, header.y_units, header.z_units) | ||
| ... print(header.title) | ||
| ... print(header.command) | ||
| ... print(header.remark) | ||
| ... print(header.nm, header.size, header.complex_mode) | ||
| ... print(header.type, header.n_bands, header.mx, header.my) | ||
| ... print(header.pad[:]) | ||
| ... print(header.mem_layout, header.nan_value, header.xy_off) | ||
| ... # Image-specific attributes. | ||
| ... print(image.type, image.n_indexed_colors) | ||
| ... # The x and y coordinates | ||
| ... x = image.x[: header.n_columns] | ||
| ... y = image.y[: header.n_rows] | ||
| ... # The data array (with paddings) | ||
| ... data = np.reshape( | ||
| ... image.data[: header.n_bands * header.mx * header.my], | ||
| ... (header.my, header.mx, header.n_bands), | ||
| ... ) | ||
| ... # The data array (without paddings) | ||
| ... pad = header.pad[:] | ||
| ... data = data[pad[2] : header.my - pad[3], pad[0] : header.mx - pad[1], :] | ||
| 180 360 1 | ||
| [-180.0, 180.0, -90.0, 90.0] [1.0, 1.0] | ||
| 1.0 0.0 | ||
| b'x' b'y' b'z' | ||
| b'' | ||
| b'' | ||
| b'' | ||
| 64800 66976 0 | ||
| 0 3 364 184 | ||
| [2, 2, 2, 2] | ||
| b'BRPa' 0.0 0.5 | ||
| 1 0 | ||
| >>> x | ||
| [-179.5, -178.5, ..., 178.5, 179.5] | ||
| >>> y | ||
| [89.5, 88.5, ..., -88.5, -89.5] | ||
| >>> data.shape | ||
| (180, 360, 3) | ||
| >>> data.min(), data.max() | ||
| (10, 255) | ||
| """ | ||
|
|
||
| _fields_: ClassVar = [ | ||
| # Data type, e.g. GMT_FLOAT | ||
| ("type", ctp.c_int), | ||
| # Array with color lookup values | ||
| ("colormap", ctp.POINTER(ctp.c_int)), | ||
| # Number of colors in a paletted image | ||
| ("n_indexed_colors", ctp.c_int), | ||
| # Pointer to full GMT header for the image | ||
| ("header", ctp.POINTER(_GMT_GRID_HEADER)), | ||
| # Pointer to actual image | ||
| ("data", ctp.POINTER(ctp.c_ubyte)), | ||
| # Pointer to an optional transparency layer stored in a separate variable | ||
| ("alpha", ctp.POINTER(ctp.c_ubyte)), | ||
| # Color interpolation | ||
| ("color_interp", ctp.c_char_p), | ||
| # Pointer to the x-coordinate vector | ||
| ("x", ctp.POINTER(ctp.c_double)), | ||
| # Pointer to the y-coordinate vector | ||
| ("y", ctp.POINTER(ctp.c_double)), | ||
| # Book-keeping variables "hidden" from the API | ||
| ("hidden", ctp.c_void_p), | ||
| ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -132,6 +132,60 @@ def test_clib_read_data_grid_actual_image(): | |
| ) | ||
|
|
||
|
|
||
| # Note: Simplify the tests for images after GMT_IMAGE.to_dataarray() is implemented. | ||
| def test_clib_read_data_image(): | ||
| """ | ||
| Test the Session.read_data method for images. | ||
| """ | ||
| with Session() as lib: | ||
| image = lib.read_data("@earth_day_01d_p", kind="image").contents | ||
| header = image.header.contents | ||
| assert header.n_rows == 180 | ||
| assert header.n_columns == 360 | ||
| assert header.n_bands == 3 | ||
| assert header.wesn[:] == [-180.0, 180.0, -90.0, 90.0] | ||
| assert image.data | ||
|
|
||
|
|
||
| def test_clib_read_data_image_two_steps(): | ||
| """ | ||
| Test the Session.read_data method for images in two steps, first reading the header | ||
| and then the data. | ||
| """ | ||
| infile = "@earth_day_01d_p" | ||
| with Session() as lib: | ||
| # Read the header first | ||
| data_ptr = lib.read_data(infile, kind="image", mode="GMT_CONTAINER_ONLY") | ||
| image = data_ptr.contents | ||
| header = image.header.contents | ||
| assert header.n_rows == 180 | ||
| assert header.n_columns == 360 | ||
| assert header.wesn[:] == [-180.0, 180.0, -90.0, 90.0] | ||
| assert header.n_bands == 3 # Explicitly check n_bands | ||
| assert not image.data # The data is not read yet | ||
|
|
||
| # Read the data | ||
| lib.read_data(infile, kind="image", mode="GMT_DATA_ONLY", data=data_ptr) | ||
| assert image.data | ||
|
|
||
|
|
||
| def test_clib_read_data_image_actual_grid(): | ||
| """ | ||
| Test the Session.read_data method for image, but actually the file is a grid. | ||
| """ | ||
| with Session() as lib: | ||
| data_ptr = lib.read_data( | ||
| "@earth_relief_01d_p", kind="image", mode="GMT_CONTAINER_ONLY" | ||
| ) | ||
|
||
| image = data_ptr.contents | ||
| header = image.header.contents | ||
| assert header.n_rows == 180 | ||
| assert header.n_columns == 360 | ||
| assert header.wesn[:] == [-179.5, 179.5, -89.5, 89.5] | ||
| # Explicitly check n_bands. Grid has only one band. | ||
| assert header.n_bands == 1 | ||
|
|
||
|
|
||
| def test_clib_read_data_fails(): | ||
| """ | ||
| Test that the Session.read_data method raises an exception if there are errors. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.