Skip to content

Commit

Permalink
Merge branch 'main' into make-lists-consistent
Browse files Browse the repository at this point in the history
  • Loading branch information
yvonnefroehlich authored Nov 6, 2024
2 parents 2aa4e4f + 40edcf7 commit 3f52648
Show file tree
Hide file tree
Showing 20 changed files with 167 additions and 160 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ jobs:

# Run the benchmark tests
- name: Run benchmarks
uses: CodSpeedHQ/action@v3.0.1
uses: CodSpeedHQ/action@v3.1.0
with:
# 'bash -el -c' is needed to use the custom shell.
# See https://github.com/CodSpeedHQ/action/issues/65.
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish-to-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ jobs:
ls -lh dist/
- name: Publish to Test PyPI
uses: pypa/gh-action-pypi-publish@v1.10.3
uses: pypa/gh-action-pypi-publish@v1.12.0
with:
repository-url: https://test.pypi.org/legacy/

- name: Publish to PyPI
if: startsWith(github.ref, 'refs/tags')
uses: pypa/gh-action-pypi-publish@v1.10.3
uses: pypa/gh-action-pypi-publish@v1.12.0
10 changes: 5 additions & 5 deletions doc/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ import pygmt
pygmt.show_versions()
```

You are now ready to make your first figure! Start by looking at our [Intro](examples/intro/index.rst),
[Tutorials](examples/tutorials/index.rst), and [Gallery](examples/gallery/index.rst). Good luck!
You are now ready to make your first figure! Start by looking at our [Intro](intro/index.rst),
[Tutorials](tutorials/index.rst), and [Gallery](gallery/index.rst). Good luck!

:::{note}
The sections below provide more detailed, step by step instructions to install and test
Expand Down Expand Up @@ -231,7 +231,7 @@ python -m pip install pygmt
```

::: {tip}
You can also run `python -m pip install pygmt[all]` to install pygmt with all of its
You can also run `python -m pip install pygmt[all]` to install PyGMT with all of its
optional dependencies.
:::

Expand Down Expand Up @@ -303,8 +303,8 @@ C:\Users\USERNAME\Miniforge3\envs\pygmt\Library\bin\

### `ModuleNotFoundError` in Jupyter notebook environment

If you can successfully import pygmt in a Python interpreter or IPython, but get a
`ModuleNotFoundError` when importing pygmt in Jupyter, you may need to activate your
If you can successfully import PyGMT in a Python interpreter or IPython, but get a
`ModuleNotFoundError` when importing PyGMT in Jupyter, you may need to activate your
`pygmt` virtual environment (using `mamba activate pygmt` or `conda activate pygmt`)
and install a `pygmt` kernel following the commands below:
```
Expand Down
4 changes: 3 additions & 1 deletion examples/gallery/symbols/datetime_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@
)

# numpy.datetime64 types
x = np.array(["2010-06-01", "2011-06-01T12", "2012-01-01T12:34:56"], dtype="datetime64")
x = np.array(
["2010-06-01", "2011-06-01T12", "2012-01-01T12:34:56"], dtype=np.datetime64
)
y = [1, 2, 3]
fig.plot(x=x, y=y, style="c0.4c", pen="1p", fill="red3")

Expand Down
4 changes: 3 additions & 1 deletion examples/tutorials/advanced/date_time_charts.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,9 @@
# before passing it as an argument. However, ``np.array`` objects use less memory and
# allow developers to specify data types.

x = np.array(["2010-06-01", "2011-06-01T12", "2012-01-01T12:34:56"], dtype="datetime64")
x = np.array(
["2010-06-01", "2011-06-01T12", "2012-01-01T12:34:56"], dtype=np.datetime64
)
y = [2, 7, 5]

fig = pygmt.Figure()
Expand Down
68 changes: 47 additions & 21 deletions pygmt/clib/conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,52 @@ def dataarray_to_matrix(
return matrix, region, inc


def _to_numpy(data: Any) -> np.ndarray:
"""
Convert an array-like object to a C contiguous NumPy array.
The function aims to convert any array-like objects (e.g., Python lists or tuples,
NumPy arrays with various dtypes, pandas.Series with NumPy/pandas/PyArrow dtypes,
PyArrow arrays with various dtypes) to a NumPy array.
The function is internally used in the ``vectors_to_arrays`` function, which is
responsible for converting a sequence of vectors to a list of C contiguous NumPy
arrays. Thus, the function uses the :numpy:func:`numpy.ascontiguousarray` function
rather than the :numpy:func:`numpy.asarray`/:numpy::func:`numpy.asanyarray`
functions, to ensure the returned NumPy array is C contiguous.
Parameters
----------
data
The array-like object to convert.
Returns
-------
array
The C contiguous NumPy array.
"""
# Mapping of unsupported dtypes to the expected NumPy dtype.
dtypes: dict[str, type] = {
"date32[day][pyarrow]": np.datetime64,
"date64[ms][pyarrow]": np.datetime64,
}

if (
hasattr(data, "isna")
and data.isna().any()
and Version(pd.__version__) < Version("2.2")
):
# Workaround for dealing with pd.NA with pandas < 2.2.
# Bug report at: https://github.com/GenericMappingTools/pygmt/issues/2844
# Following SPEC0, pandas 2.1 will be dropped in 2025 Q3, so it's likely
# we can remove the workaround in PyGMT v0.17.0.
array = np.ascontiguousarray(data.astype(float))
else:
vec_dtype = str(getattr(data, "dtype", ""))
array = np.ascontiguousarray(data, dtype=dtypes.get(vec_dtype))
return array


def vectors_to_arrays(vectors: Sequence[Any]) -> list[np.ndarray]:
"""
Convert 1-D vectors (scalars, lists, or array-like) to C contiguous 1-D arrays.
Expand Down Expand Up @@ -171,27 +217,7 @@ def vectors_to_arrays(vectors: Sequence[Any]) -> list[np.ndarray]:
>>> all(i.ndim == 1 for i in arrays)
True
"""
dtypes = {
"date32[day][pyarrow]": np.datetime64,
"date64[ms][pyarrow]": np.datetime64,
}
arrays = []
for vector in vectors:
if (
hasattr(vector, "isna")
and vector.isna().any()
and Version(pd.__version__) < Version("2.2")
):
# Workaround for dealing with pd.NA with pandas < 2.2.
# Bug report at: https://github.com/GenericMappingTools/pygmt/issues/2844
# Following SPEC0, pandas 2.1 will be dropped in 2025 Q3, so it's likely
# we can remove the workaround in PyGMT v0.17.0.
array = np.ascontiguousarray(vector.astype(float))
else:
vec_dtype = str(getattr(vector, "dtype", ""))
array = np.ascontiguousarray(vector, dtype=dtypes.get(vec_dtype))
arrays.append(array)
return arrays
return [_to_numpy(vector) for vector in vectors]


def sequence_to_ctypes_array(
Expand Down
17 changes: 10 additions & 7 deletions pygmt/clib/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,12 @@
np.int16: "GMT_SHORT",
np.int32: "GMT_INT",
np.int64: "GMT_LONG",
np.longlong: "GMT_LONG",
np.uint8: "GMT_UCHAR",
np.uint16: "GMT_USHORT",
np.uint32: "GMT_UINT",
np.uint64: "GMT_ULONG",
np.ulonglong: "GMT_ULONG",
np.float32: "GMT_FLOAT",
np.float64: "GMT_DOUBLE",
np.timedelta64: "GMT_LONG",
Expand Down Expand Up @@ -910,12 +912,12 @@ def _check_dtype_and_dim(self, array: np.ndarray, ndim: int) -> int:
Examples
--------
>>> import numpy as np
>>> data = np.array([1, 2, 3], dtype="float64")
>>> data = np.array([1, 2, 3], dtype=np.float64)
>>> with Session() as lib:
... gmttype = lib._check_dtype_and_dim(data, ndim=1)
... gmttype == lib["GMT_DOUBLE"]
True
>>> data = np.ones((5, 2), dtype="float32")
>>> data = np.ones((5, 2), dtype=np.float32)
>>> with Session() as lib:
... gmttype = lib._check_dtype_and_dim(data, ndim=2)
... gmttype == lib["GMT_FLOAT"]
Expand Down Expand Up @@ -948,8 +950,9 @@ def put_vector(self, dataset: ctp.c_void_p, column: int, vector: np.ndarray):
The dataset must be created by :meth:`pygmt.clib.Session.create_data` first with
``family="GMT_IS_DATASET|GMT_VIA_VECTOR"``.
Not all numpy dtypes are supported, only: int8, int16, int32, int64, uint8,
uint16, uint32, uint64, float32, float64, str\_, and datetime64.
Not all numpy dtypes are supported, only: int8, int16, int32, int64, longlong,
uint8, uint16, uint32, uint64, ulonglong, float32, float64, str\_, datetime64,
and timedelta64.
.. warning::
The numpy array must be C contiguous in memory. Use
Expand Down Expand Up @@ -1060,8 +1063,8 @@ def put_matrix(self, dataset: ctp.c_void_p, matrix: np.ndarray, pad: int = 0):
The dataset must be created by :meth:`pygmt.clib.Session.create_data` first with
``family="GMT_IS_DATASET|GMT_VIA_MATRIX"``.
Not all numpy dtypes are supported, only: int8, int16, int32, int64, uint8,
uint16, uint32, uint64, float32, and float64.
Not all numpy dtypes are supported, only: int8, int16, int32, int64, longlong,
uint8, uint16, uint32, uint64, ulonglong, float32, and float64.
.. warning::
The numpy array must be C contiguous in memory. Use
Expand Down Expand Up @@ -1505,7 +1508,7 @@ def virtualfile_from_vectors(
strings = np.array(
[" ".join(vals) for vals in zip(*string_arrays, strict=True)]
)
strings = np.asanyarray(a=strings, dtype=str)
strings = np.asanyarray(a=strings, dtype=np.str_)
self.put_strings(
dataset, family="GMT_IS_VECTOR|GMT_IS_DUPLICATE", strings=strings
)
Expand Down
2 changes: 1 addition & 1 deletion pygmt/datatypes/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def to_strings(self) -> np.ndarray[Any, np.dtype[np.str_]]:
)
warnings.warn(msg, category=RuntimeWarning, stacklevel=1)
textvector = [item if item is not None else b"" for item in textvector]
return np.char.decode(textvector) if textvector else np.array([], dtype=str)
return np.char.decode(textvector) if textvector else np.array([], dtype=np.str_)

def to_dataframe(
self,
Expand Down
48 changes: 24 additions & 24 deletions pygmt/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import numpy as np
from pygmt.clib import Session
from pygmt.exceptions import GMTError, GMTInvalidInput
from pygmt.exceptions import GMTInvalidInput
from pygmt.helpers import launch_external_viewer, unique_name


Expand Down Expand Up @@ -135,7 +135,7 @@ def region(self) -> np.ndarray:
wesn = lib.extract_region()
return wesn

def savefig( # noqa: PLR0912
def savefig(
self,
fname: str | PurePath,
transparent: bool = False,
Expand Down Expand Up @@ -177,7 +177,8 @@ def savefig( # noqa: PLR0912
The desired figure file name, including the extension. See the list of
supported formats and their extensions above.
transparent
Use a transparent background for the figure. Only valid for PNG format.
Use a transparent background for the figure. Only valid for PNG format and
the PNG file asscoiated with KML format.
crop
Crop the figure canvas (page) to the plot area.
anti_alias
Expand All @@ -203,9 +204,9 @@ def savefig( # noqa: PLR0912
"bmp": "b",
"eps": "e",
"jpg": "j",
"kml": "g",
"kml": "G" if transparent is True else "g",
"pdf": "f",
"png": "g",
"png": "G" if transparent is True else "g",
"ppm": "m",
"tif": "t",
"tiff": None, # GeoTIFF doesn't need the -T option
Expand All @@ -226,14 +227,12 @@ def savefig( # noqa: PLR0912
msg = "Extension '.ps' is not supported. Use '.eps' or '.pdf' instead."
raise GMTInvalidInput(msg)
case ext if ext not in fmts:
raise GMTInvalidInput(f"Unknown extension '.{ext}'.")

fmt = fmts[ext]
if transparent:
if fmt != "g":
msg = f"Transparency unavailable for '{ext}', only for png."
msg = f"Unknown extension '.{ext}'."
raise GMTInvalidInput(msg)
fmt = fmt.upper()

if transparent and ext not in {"kml", "png"}:
msg = f"Transparency unavailable for '{ext}', only for png and kml."
raise GMTInvalidInput(msg)
if anti_alias:
kwargs["Qt"] = 2
kwargs["Qg"] = 2
Expand All @@ -244,18 +243,17 @@ def savefig( # noqa: PLR0912
raise GMTInvalidInput(msg)
kwargs["W"] = True

# pytest-mpl v0.17.0 added the "metadata" parameter to Figure.savefig, which
# is not recognized. So remove it before calling Figure.psconvert.
# pytest-mpl v0.17.0 added the "metadata" parameter to Figure.savefig, which is
# not recognized. So remove it before calling Figure.psconvert.
kwargs.pop("metadata", None)
self.psconvert(prefix=prefix, fmt=fmt, crop=crop, **kwargs)
self.psconvert(prefix=prefix, fmt=fmts[ext], crop=crop, **kwargs)

# Remove the .pgw world file if exists.
# Not necessary after GMT 6.5.0.
# Remove the .pgw world file if exists. Not necessary after GMT 6.5.0.
# See upstream fix https://github.com/GenericMappingTools/gmt/pull/7865
if ext == "tiff":
fname.with_suffix(".pgw").unlink(missing_ok=True)

# Rename if file extension doesn't match the input file suffix
# Rename if file extension doesn't match the input file suffix.
if ext != suffix[1:]:
fname.with_suffix("." + ext).rename(fname)

Expand Down Expand Up @@ -331,11 +329,12 @@ def show(
match method:
case "notebook":
if not _HAS_IPYTHON:
raise GMTError(
msg = (
"Notebook display is selected, but IPython is not available. "
"Make sure you have IPython installed, "
"or run the script in a Jupyter notebook."
)
raise ImportError(msg)
png = self._preview(
fmt="png", dpi=dpi, anti_alias=True, as_bytes=True, **kwargs
)
Expand All @@ -344,14 +343,15 @@ def show(
pdf = self._preview(
fmt="pdf", dpi=dpi, anti_alias=False, as_bytes=False, **kwargs
)
launch_external_viewer(pdf, waiting=waiting) # type: ignore[arg-type]
launch_external_viewer(pdf, waiting=waiting)
case "none":
pass # Do nothing
case _:
raise GMTInvalidInput(
f"Invalid display method '{method}'. Valid values are 'external', "
"'notebook', 'none' or None."
msg = (
f"Invalid display method '{method}'. "
"Valid values are 'external', 'notebook', 'none' or None."
)
raise GMTInvalidInput(msg)

def _preview(self, fmt: str, dpi: int, as_bytes: bool = False, **kwargs):
"""
Expand Down Expand Up @@ -400,7 +400,7 @@ def _repr_html_(self):
html = '<img src="data:image/png;base64,{image}" width="{width}px">'
return html.format(image=base64_png.decode("utf-8"), width=500)

from pygmt.src import ( # type: ignore [misc]
from pygmt.src import ( # type: ignore[misc]
basemap,
coast,
colorbar,
Expand Down
4 changes: 1 addition & 3 deletions pygmt/helpers/tempfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class GMTTempFile:
... np.savetxt(tmpfile.name, (x, y, z), fmt="%.1f")
... lines = tmpfile.read()
... print(lines)
... nx, ny, nz = tmpfile.loadtxt(unpack=True, dtype=float)
... nx, ny, nz = tmpfile.loadtxt(unpack=True, dtype=np.float64)
... print(nx, ny, nz)
0.0 1.0 2.0
0.0 1.0 2.0
Expand Down Expand Up @@ -143,9 +143,7 @@ def tempfile_from_geojson(geojson):
# 32-bit integer overflow issue. Related issues:
# https://github.com/geopandas/geopandas/issues/967#issuecomment-842877704
# https://github.com/GenericMappingTools/pygmt/issues/2497

int32_info = np.iinfo(np.int32)

if Version(gpd.__version__).major < 1: # GeoPandas v0.x
# The default engine 'fiona' supports the 'schema' parameter.
if geojson.index.name is None:
Expand Down
2 changes: 1 addition & 1 deletion pygmt/src/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,6 @@ def info(data, **kwargs):
result = np.loadtxt(result.splitlines())
except ValueError:
# Load non-numerical outputs in str type, e.g. for datetime
result = np.loadtxt(result.splitlines(), dtype="str")
result = np.loadtxt(result.splitlines(), dtype=np.str_)

return result
4 changes: 2 additions & 2 deletions pygmt/src/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ def text_( # noqa: PLR0912
if name == "angle":
extra_arrays.append(arg)
else:
extra_arrays.append(np.asarray(arg, dtype=str))
extra_arrays.append(np.asarray(arg, dtype=np.str_))

# If an array of transparency is given, GMT will read it from the last numerical
# column per data record.
Expand All @@ -237,7 +237,7 @@ def text_( # noqa: PLR0912
kwargs["t"] = True

# Append text to the last column. Text must be passed in as str type.
text = np.asarray(text, dtype=str)
text = np.asarray(text, dtype=np.str_)
encoding = _check_encoding("".join(text.flatten()))
if encoding != "ascii":
text = np.vectorize(non_ascii_to_octal, excluded="encoding")(
Expand Down
Loading

0 comments on commit 3f52648

Please sign in to comment.