Skip to content

clib: Wrap the GMT API function GMT_Read_VirtualFile #2993

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

Merged
merged 7 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -313,5 +313,6 @@ Low level access (these are mostly used by the :mod:`pygmt.clib` package):
clib.Session.put_vector
clib.Session.write_data
clib.Session.open_virtualfile
clib.Session.read_virtualfile
clib.Session.extract_region
clib.Session.get_libgmt_func
68 changes: 68 additions & 0 deletions pygmt/clib/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import pathlib
import sys
import warnings
from typing import Literal

import numpy as np
import pandas as pd
Expand All @@ -21,6 +22,7 @@
vectors_to_arrays,
)
from pygmt.clib.loading import load_libgmt
from pygmt.datatypes import _GMT_DATASET, _GMT_GRID
from pygmt.exceptions import (
GMTCLibError,
GMTCLibNoSessionError,
Expand Down Expand Up @@ -1607,6 +1609,72 @@ def virtualfile_from_data( # noqa: PLR0912

return file_context

def read_virtualfile(
self, vfname: str, kind: Literal["dataset", "grid", None] = None
):
"""
Read data from a virtual file and optionally cast into a GMT data container.

Parameters
----------
vfname
Name of the virtual file to read.
kind
Cast the data into a GMT data container. Valid values are ``"dataset"``,
``"grid"`` and ``None``. If ``None``, will return a ctypes void pointer.

Examples
--------
>>> from pygmt.clib import Session
>>> from pygmt.helpers import GMTTempFile
>>>
>>> # Read dataset from a virtual file
>>> with Session() as lib:
... with GMTTempFile(suffix=".txt") as tmpfile:
... with open(tmpfile.name, mode="w") as fp:
... print("1.0 2.0 3.0 TEXT", file=fp)
... with lib.open_virtualfile(
... "GMT_IS_DATASET", "GMT_IS_PLP", "GMT_OUT", None
... ) as vfile:
... lib.call_module("read", f"{tmpfile.name} {vfile} -Td")
... # Read the virtual file as a void pointer
... void_pointer = lib.read_virtualfile(vfile)
... assert isinstance(void_pointer, int) # void pointer is an int
... # Read the virtual file as a dataset
... data_pointer = lib.read_virtualfile(vfile, kind="dataset")
... assert isinstance(data_pointer, ctp.POINTER(_GMT_DATASET))
>>>
>>> # Read grid from a virtual file
>>> with Session() as lib:
... with lib.open_virtualfile(
... "GMT_IS_GRID", "GMT_IS_SURFACE", "GMT_OUT", None
... ) as vfile:
... lib.call_module("read", f"@earth_relief_01d_g {vfile} -Tg")
... # Read the virtual file as a void pointer
... void_pointer = lib.read_virtualfile(vfile)
... assert isinstance(void_pointer, int) # void pointer is an int
... data_pointer = lib.read_virtualfile(vfile, kind="grid")
... assert isinstance(data_pointer, ctp.POINTER(_GMT_GRID))

Returns
-------
Pointer to the GMT data container. If ``kind`` is None, returns a ctypes void
pointer instead.
"""
c_read_virtualfile = self.get_libgmt_func(
"GMT_Read_VirtualFile",
argtypes=[ctp.c_void_p, ctp.c_char_p],
restype=ctp.c_void_p,
)
pointer = c_read_virtualfile(self.session_pointer, vfname.encode())
# The GMT C API function GMT_Read_VirtualFile returns a void pointer. It usually
# needs to be cast into a pointer to a GMT data container (e.g., _GMT_GRID or
# _GMT_DATASET).
if kind is None: # Return the ctypes void pointer
return pointer
dtype = {"dataset": _GMT_DATASET, "grid": _GMT_GRID}[kind]
return ctp.cast(pointer, ctp.POINTER(dtype))

def extract_region(self):
"""
Extract the WESN bounding box of the currently active figure.
Expand Down
5 changes: 5 additions & 0 deletions pygmt/datatypes/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""
Wrappers for GMT data types.
"""
from pygmt.datatypes.dataset import _GMT_DATASET
from pygmt.datatypes.grid import _GMT_GRID
Comment on lines +4 to +5
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In #2398 and #2729, you have the GMT_DATASET and GMT_GRID classes in one file, but here, you've separated them into two files. I think this is ok, since we'll need to wrap GMT_IMAGE and GMT_CUBE at some point.

8 changes: 8 additions & 0 deletions pygmt/datatypes/dataset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""
Wrapper for the GMT_DATASET data type.
"""
import ctypes as ctp


class _GMT_DATASET(ctp.Structure): # noqa: N801
pass
8 changes: 8 additions & 0 deletions pygmt/datatypes/grid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""
Wrapper for the GMT_GRID data type.
"""
import ctypes as ctp


class _GMT_GRID(ctp.Structure): # noqa: N801
pass