From 0169b9d8625f26cda9f2cc5f9f702a96e7bd8555 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 6 Nov 2024 10:31:51 +0800 Subject: [PATCH 1/5] Test timedelta64 dtype arrays with various date/time units (#3567) --- pygmt/tests/test_clib_put_vector.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pygmt/tests/test_clib_put_vector.py b/pygmt/tests/test_clib_put_vector.py index 46b199727f0..968e09bf87c 100644 --- a/pygmt/tests/test_clib_put_vector.py +++ b/pygmt/tests/test_clib_put_vector.py @@ -172,10 +172,12 @@ def test_put_vector_string_dtype(): def test_put_vector_timedelta64_dtype(): """ - Passing timedelta64 type vectors with various time units (year, month, - week, day, hour, minute, second, millisecond, microsecond) to a dataset. + Passing timedelta64 type vectors with various date/time units to a dataset. + + Valid date/time units can be found at + https://numpy.org/devdocs/reference/arrays.datetime.html#datetime-units. """ - for unit in ["Y", "M", "W", "D", "h", "m", "s", "ms", "μs"]: + for unit in ["Y", "M", "W", "D", "h", "m", "s", "ms", "us", "ns", "ps", "fs", "as"]: with clib.Session() as lib, GMTTempFile() as tmp_file: dataset = lib.create_data( family="GMT_IS_DATASET|GMT_VIA_VECTOR", From 38694be57b209405a292440684ed54cfa4c4c7e9 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 6 Nov 2024 11:39:26 +0800 Subject: [PATCH 2/5] Figure.show: Raise ImportError instead of GMTError if IPython is not installed but required (#3580) --- pygmt/figure.py | 16 +++++++++------- pygmt/tests/test_figure.py | 6 +++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/pygmt/figure.py b/pygmt/figure.py index 9c0a7a7a763..0b2faea00d0 100644 --- a/pygmt/figure.py +++ b/pygmt/figure.py @@ -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 @@ -331,11 +331,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 ) @@ -344,14 +345,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): """ @@ -400,7 +402,7 @@ def _repr_html_(self): html = '' 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, diff --git a/pygmt/tests/test_figure.py b/pygmt/tests/test_figure.py index f91509dd8f2..10076b69437 100644 --- a/pygmt/tests/test_figure.py +++ b/pygmt/tests/test_figure.py @@ -12,7 +12,7 @@ import numpy.testing as npt import pytest from pygmt import Figure, set_display -from pygmt.exceptions import GMTError, GMTInvalidInput +from pygmt.exceptions import GMTInvalidInput from pygmt.figure import SHOW_CONFIG, _get_default_display_method from pygmt.helpers import GMTTempFile @@ -321,7 +321,7 @@ def test_figure_show_notebook_error_without_ipython(): """ fig = Figure() fig.basemap(region=[0, 1, 2, 3], frame=True) - with pytest.raises(GMTError): + with pytest.raises(ImportError): fig.show(method="notebook") @@ -361,7 +361,7 @@ def test_set_display(self): assert mock_viewer.call_count == 0 assert mock_display.call_count == 1 else: - with pytest.raises(GMTError): + with pytest.raises(ImportError): fig.show() # Test the "external" display method From 40edcf7123537754376f7e1960cdd64a6e3cf8ac Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 6 Nov 2024 11:39:42 +0800 Subject: [PATCH 3/5] Figure.savefig: Clarify that 'transparent' also works for the PNG file associated with the KML format (#3579) --- pygmt/figure.py | 32 +++++++++++++++----------------- pygmt/tests/test_figure.py | 11 ++++++++++- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/pygmt/figure.py b/pygmt/figure.py index 0b2faea00d0..4163ab52eb1 100644 --- a/pygmt/figure.py +++ b/pygmt/figure.py @@ -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, @@ -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 @@ -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 @@ -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 @@ -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) diff --git a/pygmt/tests/test_figure.py b/pygmt/tests/test_figure.py index 10076b69437..1831961ced4 100644 --- a/pygmt/tests/test_figure.py +++ b/pygmt/tests/test_figure.py @@ -196,12 +196,21 @@ def test_figure_savefig_transparent(): fname = f"{prefix}.{fmt}" with pytest.raises(GMTInvalidInput): fig.savefig(fname, transparent=True) - # png should not raise an error + + # PNG should support transparency and should not raise an error. fname = Path(f"{prefix}.png") fig.savefig(fname, transparent=True) assert fname.exists() fname.unlink() + # The companion PNG file with KML format should also support transparency. + fname = Path(f"{prefix}.kml") + fig.savefig(fname, transparent=True) + assert fname.exists() + fname.unlink() + assert fname.with_suffix(".png").exists() + fname.with_suffix(".png").unlink() + def test_figure_savefig_filename_with_spaces(): """ From 4498f71e29b7997033a481de72fb2b2d74aa5bac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yvonne=20Fr=C3=B6hlich?= <94163266+yvonnefroehlich@users.noreply.github.com> Date: Wed, 6 Nov 2024 08:56:33 +0100 Subject: [PATCH 4/5] DOC/tutorial "Plotting lines": General improvements (#3587) Co-authored-by: Dongdong Tian Co-authored-by: Michael Grund <23025878+michaelgrund@users.noreply.github.com> --- examples/tutorials/basics/lines.py | 130 +++++++++++------------------ 1 file changed, 50 insertions(+), 80 deletions(-) diff --git a/examples/tutorials/basics/lines.py b/examples/tutorials/basics/lines.py index 9445ca94e13..c0cbabbdd75 100644 --- a/examples/tutorials/basics/lines.py +++ b/examples/tutorials/basics/lines.py @@ -2,7 +2,7 @@ Plotting lines ============== -Plotting lines is handled by :meth:`pygmt.Figure.plot`. +Plotting lines is handled by the :meth:`pygmt.Figure.plot` method. """ # %% @@ -12,51 +12,39 @@ # Plot lines # ---------- # -# Create a Cartesian figure using ``projection`` parameter and set the axis -# scales using ``region`` (in this case, each axis is 0-10). Pass a list of -# ``x`` and ``y`` values to be plotted as a line. +# Create a Cartesian figure using the :meth:`pygmt.Figure.basemap` method. Pass lists +# containing two values to the ``x`` and ``y`` parameters of the +# :meth:`pygmt.Figure.plot` method. By default, a 0.25-points thick, black, solid +# line is drawn between these two data points. fig = pygmt.Figure() -fig.plot( - region=[0, 10, 0, 10], - projection="X15c/10c", - frame="a", - x=[1, 8], - y=[5, 9], - pen="1p,black", -) +fig.basemap(region=[0, 10, 0, 10], projection="X15c/10c", frame=True) + +fig.plot(x=[1, 5], y=[5, 9]) + fig.show() # %% -# Additional line segments can be added by including additional values for -# ``x`` and ``y``. +# Additional line segments can be added by including more data points in the lists +# passed to ``x`` and ``y``. fig = pygmt.Figure() -fig.plot( - region=[0, 10, 0, 10], - projection="X15c/10c", - frame="a", - x=[1, 6, 9], - y=[5, 7, 4], - pen="1p,black", -) +fig.basemap(region=[0, 10, 0, 10], projection="X15c/10c", frame=True) + +fig.plot(x=[1, 5, 8], y=[5, 9, 4]) + fig.show() # %% -# To plot multiple lines, :meth:`pygmt.Figure.plot` needs to be used for each -# additional line. Parameters such as ``region``, ``projection``, and ``frame`` -# do not need to be repeated in subsequent uses. +# To plot multiple lines, :meth:`pygmt.Figure.plot` needs to be used for each line +# separately. fig = pygmt.Figure() -fig.plot( - region=[0, 10, 0, 10], - projection="X15c/10c", - frame="a", - x=[1, 6, 9], - y=[5, 7, 4], - pen="2p,blue", -) -fig.plot(x=[2, 4, 10], y=[3, 8, 9], pen="2p,red") +fig.basemap(region=[0, 10, 0, 10], projection="X15c/10c", frame=True) + +fig.plot(x=[1, 5, 8], y=[5, 9, 4]) +fig.plot(x=[2, 4, 9], y=[3, 7, 5]) + fig.show() @@ -64,71 +52,53 @@ # Change line attributes # ---------------------- # -# The line attributes can be set by the ``pen`` parameter. ``pen`` takes a -# string argument with the optional values *width*,\ *color*,\ *style*. -# -# In the example below, the pen width is set to ``5p``, and with ``black`` as -# the default color and ``solid`` as the default style. +# The line attributes can be set by the ``pen`` parameter which takes a string +# argument with the optional values *width*,\ *color*,\ *style*. fig = pygmt.Figure() -fig.plot( - region=[0, 10, 0, 10], - projection="X15c/10c", - frame="a", - x=[1, 8], - y=[3, 9], - pen="5p", -) +fig.basemap(region=[0, 10, 0, 10], projection="X15c/10c", frame=True) + +# Set the pen width to "5p" (5 points), and use the default color "black" and the +# default style "solid" +fig.plot(x=[1, 8], y=[3, 9], pen="5p") + fig.show() # %% # The line color can be set and is added after the line width to the ``pen`` -# parameter. In the example below, the line color is set to ``red``. +# parameter. fig = pygmt.Figure() -fig.plot( - region=[0, 10, 0, 10], - projection="X15c/10c", - frame="a", - x=[1, 8], - y=[3, 9], - pen="5p,red", -) +fig.basemap(region=[0, 10, 0, 10], projection="X15c/10c", frame=True) + +# Set the line color to "red", use the default style "solid" +fig.plot(x=[1, 8], y=[3, 9], pen="5p,red") + fig.show() # %% # The line style can be set and is added after the line width or color to the -# ``pen`` parameter. In the example below, the line style is set to -# ``..-`` (*dot dot dash*), and the default color ``black`` is used. +# ``pen`` parameter. fig = pygmt.Figure() -fig.plot( - region=[0, 10, 0, 10], - projection="X15c/10c", - frame="a", - x=[1, 8], - y=[3, 9], - pen="5p,..-", -) +fig.basemap(region=[0, 10, 0, 10], projection="X15c/10c", frame=True) + +# Set the line style to "..-" (dot dot dash), use the default color "black" +fig.plot(x=[1, 8], y=[3, 9], pen="5p,..-") + fig.show() # %% -# The line width, color, and style can all be set in the same ``pen`` -# parameter. In the example below, the line width is set to ``7p``, the color -# is set to ``green``, and the line style is ``-.-`` (*dash dot dash*). -# -# For a gallery showing other ``pen`` settings, see +# The line width, color, and style can all be set in the same ``pen`` parameter. +# For a gallery example showing other ``pen`` settings, see # :doc:`/gallery/lines/linestyles`. fig = pygmt.Figure() -fig.plot( - region=[0, 10, 0, 10], - projection="X15c/10c", - frame="a", - x=[1, 8], - y=[3, 9], - pen="7p,green,-.-", -) +fig.basemap(region=[0, 10, 0, 10], projection="X15c/10c", frame=True) + +# Draw a 7-points thick, green line with style "-.-" (dash dot dash) +fig.plot(x=[1, 8], y=[3, 9], pen="7p,green,-.-") + fig.show() -# sphinx_gallery_thumbnail_number = 3 +# sphinx_gallery_thumbnail_number = 6 From c50c1ec5f26077b39ebfe3f833d42b51f43cee6c Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 7 Nov 2024 06:32:35 +0800 Subject: [PATCH 5/5] Add PyArrow as an optional dependency (#3592) Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com> --- .github/workflows/ci_docs.yml | 1 + .github/workflows/type_checks.yml | 4 ++-- ci/requirements/docs.yml | 1 + environment.yml | 1 + pyproject.toml | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index afc4309122e..475230e9c17 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -103,6 +103,7 @@ jobs: contextily geopandas<1.0 ipython + pyarrow rioxarray make pip diff --git a/.github/workflows/type_checks.yml b/.github/workflows/type_checks.yml index c9feffd39db..525735d72b1 100644 --- a/.github/workflows/type_checks.yml +++ b/.github/workflows/type_checks.yml @@ -50,8 +50,8 @@ jobs: # 4. other packages that are used somewhere in PyGMT python -m pip install \ numpy pandas xarray netcdf4 packaging \ - contextily geopandas ipython rioxarray \ - mypy pandas-stubs \ + contextily geopandas ipython pyarrow rioxarray \ + mypy pandas-stubs pyarrow-stubs \ matplotlib pytest python -m pip list diff --git a/ci/requirements/docs.yml b/ci/requirements/docs.yml index 8214dbac8fe..6ae1595fe28 100644 --- a/ci/requirements/docs.yml +++ b/ci/requirements/docs.yml @@ -16,6 +16,7 @@ dependencies: - contextily - geopandas<1.0 - ipython + - pyarrow - rioxarray # Development dependencies (general) - make diff --git a/environment.yml b/environment.yml index 85d7c845866..c63829d974f 100644 --- a/environment.yml +++ b/environment.yml @@ -16,6 +16,7 @@ dependencies: - contextily - geopandas - ipython + - pyarrow - rioxarray # Development dependencies (general) - dvc diff --git a/pyproject.toml b/pyproject.toml index 8819d4e7b1b..8400da80625 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,7 @@ all = [ "contextily", "geopandas", "IPython", # 'ipython' is not the correct module name. + "pyarrow", "rioxarray", ]