From d54e6e1f14d85ece2232cb32072ed38c5ee801e8 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 2 Jan 2025 18:40:04 +0800 Subject: [PATCH 01/35] Update License year to 2025 (#3737) --- LICENSE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.txt b/LICENSE.txt index 6411f912cea..c6c569c4bc6 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2017-2024 The PyGMT Developers +Copyright (c) 2017-2025 The PyGMT Developers All rights reserved. Redistribution and use in source and binary forms, with or without modification, From e3a3682dafb440a6856dce6b8df3682710654665 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 11:08:17 +1300 Subject: [PATCH 02/35] Build(deps): Bump astral-sh/setup-uv from 4.2.0 to 5.1.0 (#3735) * Build(deps): Bump astral-sh/setup-uv from 4.2.0 to 5.1.0 Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 4.2.0 to 5.1.0. - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](https://github.com/astral-sh/setup-uv/compare/v4.2.0...v5.1.0) --- updated-dependencies: - dependency-name: astral-sh/setup-uv dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * Try the venv auto-activate feature in v5.0.0 * Put `uv run dvc pull` command on one line --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Dongdong Tian Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com> --- .github/workflows/ci_tests.yaml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index 2d9091ab384..e155d7bd95c 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -154,20 +154,18 @@ jobs: GH_TOKEN: ${{ github.token }} - name: Install uv - uses: astral-sh/setup-uv@v4.2.0 + uses: astral-sh/setup-uv@v5.1.0 + with: + python-version: ${{ matrix.python-version }} - name: Install dvc run: | - uv venv - source .venv/bin/activate uv pip install dvc uv pip list # Pull baseline image data from dvc remote (DAGsHub) - name: Pull baseline image data from dvc remote - run: | - source .venv/bin/activate - uv run dvc pull --no-run-cache --verbose && ls -lhR pygmt/tests/baseline/ + run: uv run dvc pull --no-run-cache --verbose && ls -lhR pygmt/tests/baseline/ # Install the package that we want to test - name: Install the package From b1c984ad0f29f49eda961bcbe843b30fbc475e00 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Fri, 3 Jan 2025 08:29:40 +0800 Subject: [PATCH 03/35] Fix an image in README.md (broken on PyPI) and rewrap to 88 characters (#3740) --- README.md | 75 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index edcf9959769..ba4e47d8fdb 100644 --- a/README.md +++ b/README.md @@ -22,22 +22,22 @@ ## Why PyGMT? -A beautiful map is worth a thousand words. To truly understand how powerful PyGMT is, play with it online on -[Binder](https://github.com/GenericMappingTools/try-gmt)! For a quicker introduction, check out our -[3 minute overview](https://youtu.be/4iPnITXrxVU)! +A beautiful map is worth a thousand words. To truly understand how powerful PyGMT is, +play with it online on [Binder](https://github.com/GenericMappingTools/try-gmt)! For a +quicker introduction, check out our [3 minute overview](https://youtu.be/4iPnITXrxVU)! -Afterwards, feel free to look at our [Tutorials](https://www.pygmt.org/latest/tutorials), visit the -[Gallery](https://www.pygmt.org/latest/gallery), and check out some +Afterwards, feel free to look at our [Tutorials](https://www.pygmt.org/latest/tutorials), +visit the [Gallery](https://www.pygmt.org/latest/gallery), and check out some [external PyGMT examples](https://www.pygmt.org/latest/external_resources.html)! -![Quick Introduction to PyGMT YouTube Video](doc/_static/scipy2022-youtube-thumbnail.jpg) +![Quick Introduction to PyGMT YouTube Video](https://raw.githubusercontent.com/GenericMappingTools/pygmt/refs/heads/main/doc/_static/scipy2022-youtube-thumbnail.jpg) ## About -PyGMT is a library for processing geospatial and geophysical data and making publication-quality -maps and figures. It provides a Pythonic interface for the -[Generic Mapping Tools (GMT)](https://github.com/GenericMappingTools/gmt), a command-line program -widely used across the Earth, Ocean, and Planetary sciences and beyond. +PyGMT is a library for processing geospatial and geophysical data and making +publication-quality maps and figures. It provides a Pythonic interface for the +[Generic Mapping Tools (GMT)](https://github.com/GenericMappingTools/gmt), a command-line +program widely used across the Earth, Ocean, and Planetary sciences and beyond. ## Project goals @@ -45,8 +45,9 @@ widely used across the Earth, Ocean, and Planetary sciences and beyond. - Build a Pythonic API for GMT. - Interface with the GMT C API directly using ctypes (no system calls). - Support for rich display in the Jupyter notebook. -- Integration with the [scientific Python ecosystem](https://scientific-python.org/): `numpy.ndarray` or - `pandas.DataFrame` for data tables, `xarray.DataArray` for grids, and `geopandas.GeoDataFrame` for geographical data. +- Integration with the [scientific Python ecosystem](https://scientific-python.org/): + `numpy.ndarray` or `pandas.DataFrame` for data tables, `xarray.DataArray` for grids, + and `geopandas.GeoDataFrame` for geographical data. ## Quickstart @@ -69,7 +70,8 @@ For other ways to install `pygmt`, see the [full installation instructions](http ### Getting started As a starting point, you can open a [Python interpreter](https://docs.python.org/3/tutorial/interpreter.html) -or a [Jupyter notebook](https://docs.jupyter.org/en/latest/running.html), and try the following example: +or a [Jupyter notebook](https://docs.jupyter.org/en/latest/running.html), and try the +following example: ``` python import pygmt @@ -79,18 +81,18 @@ fig.text(position="MC", text="PyGMT", font="80p,Helvetica-Bold,red@75") fig.show() ``` -You should see a global map with land and water masses colored in tan and lightblue, respectively. On top, -there should be the semi-transparent text "PyGMT". For more examples, please have a look at the -[Gallery](https://www.pygmt.org/latest/gallery/index.html) and +You should see a global map with land and water masses colored in tan and lightblue, +respectively. On top, there should be the semi-transparent text "PyGMT". For more examples, +please have a look at the [Gallery](https://www.pygmt.org/latest/gallery/index.html) and [Tutorials](https://www.pygmt.org/latest/tutorials/index.html). ## Contacting us - Most discussion happens [on GitHub](https://github.com/GenericMappingTools/pygmt). - Feel free to [open an issue](https://github.com/GenericMappingTools/pygmt/issues/new) or comment on any open - issue or pull request. -- We have a [Discourse forum](https://forum.generic-mapping-tools.org/c/questions/pygmt-q-a) where you can ask - questions and leave comments. + Feel free to [open an issue](https://github.com/GenericMappingTools/pygmt/issues/new) + or comment on any open issue or pull request. +- We have a [Discourse forum](https://forum.generic-mapping-tools.org/c/questions/pygmt-q-a) + where you can ask questions and leave comments. ## Contributing @@ -109,26 +111,29 @@ to see how you can help and give feedback. **We want your help.** No, really. -There may be a little voice inside your head that is telling you that you're not ready to be an open source -contributor; that your skills aren't nearly good enough to contribute. What could you possibly offer? +There may be a little voice inside your head that is telling you that you're not ready +to be an open source contributor; that your skills aren't nearly good enough to +contribute. What could you possibly offer? We assure you that the little voice in your head is wrong. -**Being a contributor doesn't just mean writing code.** Equally important contributions include: writing or -proof-reading documentation, suggesting or implementing tests, or even giving feedback about the project -(including giving feedback about the contribution process). If you're coming to the project with fresh eyes, -you might see the errors and assumptions that seasoned contributors have glossed over. If you can write any -code at all, you can contribute code to open source. We are constantly trying out new skills, making mistakes, -and learning from those mistakes. That's how we all improve and we are happy to help others learn. +**Being a contributor doesn't just mean writing code.** Equally important contributions +include: writing or proof-reading documentation, suggesting or implementing tests, or +even giving feedback about the project (including giving feedback about the contribution +process). If you're coming to the project with fresh eyes, you might see the errors and +assumptions that seasoned contributors have glossed over. If you can write any code at +all, you can contribute code to open source. We are constantly trying out new skills, +making mistakes, and learning from those mistakes. That's how we all improve and we are +happy to help others learn. *This disclaimer was adapted from the* [MetPy project](https://github.com/Unidata/MetPy). ## Citing PyGMT PyGMT is a community developed project. See the -[AUTHORS.md](https://github.com/GenericMappingTools/pygmt/blob/main/AUTHORS.md) file on GitHub for a list of -the people involved and a definition of the term "PyGMT Developers". Feel free to cite our work in your -research using the following BibTeX: +[AUTHORS.md](https://github.com/GenericMappingTools/pygmt/blob/main/AUTHORS.md) file +on GitHub for a list of the people involved and a definition of the term "PyGMT Developers". +Feel free to cite our work in your research using the following BibTeX: ``` @software{ @@ -162,10 +167,10 @@ research using the following BibTeX: ``` To cite a specific version of PyGMT, go to our Zenodo page at -and use the "Export to BibTeX" function there. It is also strongly recommended to cite the -[GMT 6 paper](https://doi.org/10.1029/2019GC008515) (which PyGMT wraps around). Note that some modules -like `dimfilter`, `surface`, and `x2sys` also have their dedicated citations. Further information for -all these can be found at . +and use the "Export to BibTeX" function there. It is also strongly recommended to cite +the [GMT 6 paper](https://doi.org/10.1029/2019GC008515) (which PyGMT wraps around). Note +that some modules like `dimfilter`, `surface`, and `x2sys` also have their dedicated +citations. Further information for all these can be found at . ## License From 4d60eac33d86ce922fda80c38755953fbf9393ab Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Fri, 3 Jan 2025 08:31:07 +0800 Subject: [PATCH 04/35] clib.Session: Remove deprecated virtualfile_from_data method, use virtualfile_in instead (Deprecated since v0.13.0) (#3739) --- doc/api/index.rst | 1 - pygmt/clib/session.py | 37 ------------------------- pygmt/tests/test_clib_virtualfile_in.py | 32 --------------------- 3 files changed, 70 deletions(-) diff --git a/doc/api/index.rst b/doc/api/index.rst index 1f573dcb8eb..25de6d44adf 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -335,7 +335,6 @@ Low level access (these are mostly used by the :mod:`pygmt.clib` package): clib.Session.read_virtualfile clib.Session.extract_region clib.Session.get_libgmt_func - clib.Session.virtualfile_from_data clib.Session.virtualfile_from_grid clib.Session.virtualfile_from_stringio clib.Session.virtualfile_from_matrix diff --git a/pygmt/clib/session.py b/pygmt/clib/session.py index 52f710ec398..b01b279241b 100644 --- a/pygmt/clib/session.py +++ b/pygmt/clib/session.py @@ -1920,43 +1920,6 @@ def virtualfile_in( file_context = _virtualfile_from(_data) return file_context - # TODO(PyGMT>=0.15.0): Remove the deprecated virtualfile_from_data method. - def virtualfile_from_data( - self, - check_kind=None, - data=None, - x=None, - y=None, - z=None, - extra_arrays=None, - required_z=False, - required_data=True, - ): - """ - Store any data inside a virtual file. - - .. deprecated: 0.13.0 - - Will be removed in v0.15.0. Use :meth:`pygmt.clib.Session.virtualfile_in` - instead. - """ - msg = ( - "API function 'Session.virtualfile_from_data()' has been deprecated since " - "v0.13.0 and will be removed in v0.15.0. Use 'Session.virtualfile_in()' " - "instead." - ) - warnings.warn(msg, category=FutureWarning, stacklevel=2) - return self.virtualfile_in( - check_kind=check_kind, - data=data, - x=x, - y=y, - z=z, - extra_arrays=extra_arrays, - required_z=required_z, - required_data=required_data, - ) - @contextlib.contextmanager def virtualfile_out( self, diff --git a/pygmt/tests/test_clib_virtualfile_in.py b/pygmt/tests/test_clib_virtualfile_in.py index 7b091fc423c..8a43c1dc273 100644 --- a/pygmt/tests/test_clib_virtualfile_in.py +++ b/pygmt/tests/test_clib_virtualfile_in.py @@ -128,35 +128,3 @@ def test_virtualfile_in_matrix_string_dtype(): assert output == "347.5 348.5 -30.5 -30\n" # Should check that lib.virtualfile_from_vectors is called once, # not lib.virtualfile_from_matrix, but it's technically complicated. - - -# TODO(PyGMT>=0.16.0): Remove this test in PyGMT v0.16.0 in which the old usage of -# virtualfile_from_data is removed. -def test_virtualfile_from_data(): - """ - Test the backwards compatibility of the virtualfile_from_data method. - - This test is the same as test_virtualfile_in_required_z_matrix, but using the - deprecated method. - """ - shape = (5, 3) - dataframe = pd.DataFrame( - data=np.arange(shape[0] * shape[1]).reshape(shape), columns=["x", "y", "z"] - ) - data = np.array(dataframe) - with clib.Session() as lib: - with pytest.warns(FutureWarning, match="virtualfile_from_data"): - with lib.virtualfile_from_data( - data=data, required_z=True, check_kind="vector" - ) as vfile: - with GMTTempFile() as outfile: - lib.call_module("info", [vfile, f"->{outfile.name}"]) - output = outfile.read(keep_tabs=True) - bounds = "\t".join( - [ - f"<{i.min():.0f}/{i.max():.0f}>" - for i in (dataframe.x, dataframe.y, dataframe.z) - ] - ) - expected = f": N = {shape[0]}\t{bounds}\n" - assert output == expected From 4b9314a5da7ef00b0d848ef09fe590d71ad38e90 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Fri, 3 Jan 2025 08:31:36 +0800 Subject: [PATCH 05/35] clib.Session: Remove deprecated open_virtual_file method, use open_virtualfile instead (Deprecated since v0.11.0) (#3738) --- pygmt/clib/session.py | 18 --------------- pygmt/tests/test_clib_virtualfiles.py | 32 --------------------------- 2 files changed, 50 deletions(-) diff --git a/pygmt/clib/session.py b/pygmt/clib/session.py index b01b279241b..f35161b7b02 100644 --- a/pygmt/clib/session.py +++ b/pygmt/clib/session.py @@ -1389,24 +1389,6 @@ def open_virtualfile( msg = f"Failed to close virtual file '{vfname}'." raise GMTCLibError(msg) - # TODO(PyGMT>=0.15.0): Remove the deprecated open_virtual_file method. - def open_virtual_file(self, family, geometry, direction, data): - """ - Open a GMT virtual file associated with a data object for reading or writing. - - .. deprecated: 0.11.0 - - Will be removed in v0.15.0. Use :meth:`pygmt.clib.Session.open_virtualfile` - instead. - """ - msg = ( - "API function `Session.open_virtual_file()' has been deprecated " - "since v0.11.0 and will be removed in v0.15.0. " - "Use `Session.open_virtualfile()' instead." - ) - warnings.warn(msg, category=FutureWarning, stacklevel=2) - return self.open_virtualfile(family, geometry, direction, data) - @contextlib.contextmanager def virtualfile_from_vectors( self, vectors: Sequence, *args diff --git a/pygmt/tests/test_clib_virtualfiles.py b/pygmt/tests/test_clib_virtualfiles.py index b25891864f6..ba48200deae 100644 --- a/pygmt/tests/test_clib_virtualfiles.py +++ b/pygmt/tests/test_clib_virtualfiles.py @@ -107,35 +107,3 @@ def test_open_virtualfile_bad_direction(): with pytest.raises(GMTInvalidInput): with lib.open_virtualfile(*vfargs): pass - - -# TODO(PyGMT>=0.15.0): Remove the test after removing the deprecated open_virtual_file -# method. -def test_open_virtual_file(): - """ - Test the deprecated Session.open_virtual_file method. - - This test is the same as test_open_virtualfile, but using the deprecated method. - """ - shape = (5, 3) - with clib.Session() as lib: - family = "GMT_IS_DATASET|GMT_VIA_MATRIX" - geometry = "GMT_IS_POINT" - dataset = lib.create_data( - family=family, - geometry=geometry, - mode="GMT_CONTAINER_ONLY", - dim=[shape[1], shape[0], 1, 0], # columns, rows, layers, dtype - ) - data = np.arange(shape[0] * shape[1]).reshape(shape) - lib.put_matrix(dataset, matrix=data) - # Add the dataset to a virtual file and pass it along to gmt info - with pytest.warns(FutureWarning, match="open_virtual_file"): - vfargs = (family, geometry, "GMT_IN|GMT_IS_REFERENCE", dataset) - with lib.open_virtual_file(*vfargs) as vfile: - with GMTTempFile() as outfile: - lib.call_module("info", [vfile, f"->{outfile.name}"]) - output = outfile.read(keep_tabs=True) - bounds = "\t".join([f"<{col.min():.0f}/{col.max():.0f}>" for col in data.T]) - expected = f": N = {shape[0]}\t{bounds}\n" - assert output == expected From af66e2ab85da2f0a1c7b7f2d64f0927ddf5d6cd4 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 6 Jan 2025 00:05:44 +0800 Subject: [PATCH 06/35] Add the missing separators in changelog (#3745) --- doc/changes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/changes.md b/doc/changes.md index 21250127374..8e7142d2fd8 100644 --- a/doc/changes.md +++ b/doc/changes.md @@ -102,6 +102,8 @@ * [Will Schlitzer](https://github.com/willschlitzer) * [Jiayuan Yao](https://github.com/core-man) +--- + ## Release v0.13.0 (2024/09/05) [![Digital Object Identifier for PyGMT v0.13.0](https://zenodo.org/badge/DOI/10.5281/zenodo.13679420.svg)](https://doi.org/10.5281/zenodo.13679420) @@ -280,6 +282,8 @@ * [Michael Grund](https://github.com/michaelgrund) * [Wei Ji Leong](https://github.com/weiji14) +--- + ## Release v0.11.0 (2024/02/01) [![Digital Object Identifier for PyGMT v0.11.0](https://zenodo.org/badge/DOI/10.5281/zenodo.10578540.svg)](https://doi.org/10.5281/zenodo.10578540) From 7c38ce75df74695e9add69fc97c38a9c7215f354 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 6 Jan 2025 08:28:20 +0800 Subject: [PATCH 07/35] Use well-known labels in project URLs following PEP753 (#3743) --- pyproject.toml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2150af08d7b..9af670df32c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,10 +48,11 @@ all = [ ] [project.urls] -homepage = "https://www.pygmt.org" -documentation = "https://www.pygmt.org" -repository = "https://github.com/GenericMappingTools/pygmt" -changelog = "https://www.pygmt.org/latest/changes.html" +"Homepage" = "https://www.pygmt.org" +"Documentation" = "https://www.pygmt.org" +"Source Code" = "https://github.com/GenericMappingTools/pygmt" +"Changelog" = "https://www.pygmt.org/latest/changes.html" +"Issue Tracker" = "https://github.com/GenericMappingTools/pygmt/issues" [tool.setuptools] platforms = ["Any"] From 4c03bab6fbc90f7354fad28d663fd98f9df6c268 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 6 Jan 2025 22:57:33 +0800 Subject: [PATCH 08/35] Update the release checklist post v0.14.0 release (#3692) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com> Co-authored-by: Michael Grund <23025878+michaelgrund@users.noreply.github.com> Co-authored-by: Yvonne Fröhlich <94163266+yvonnefroehlich@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/4-release_checklist.md | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/4-release_checklist.md b/.github/ISSUE_TEMPLATE/4-release_checklist.md index 83fe809724a..87a164b15c8 100644 --- a/.github/ISSUE_TEMPLATE/4-release_checklist.md +++ b/.github/ISSUE_TEMPLATE/4-release_checklist.md @@ -19,17 +19,15 @@ assignees: '' **Before release**: -- [ ] Check [SPEC 0](https://scientific-python.org/specs/spec-0000/) to see if we need to bump the minimum supported versions of GMT, Python and - core package dependencies (NumPy, pandas, Xarray) +- [ ] Check [SPEC 0](https://scientific-python.org/specs/spec-0000/) to see if we need to bump the minimum supported versions of GMT, Python and core package dependencies (NumPy, pandas, Xarray) - [ ] Review the ["PyGMT Team" page](https://www.pygmt.org/dev/team.html) +- [ ] README looks good on TestPyPI. Visit [TestPyPI](https://test.pypi.org/project/pygmt/#history), click the latest pre-release, and check the homepage. - [ ] Check to ensure that: - [ ] Deprecated workarounds/codes/tests are removed. Run `grep "# TODO" **/*.py` to find all potential TODOs. - [ ] All tests pass in the ["GMT Legacy Tests" workflow](https://github.com/GenericMappingTools/pygmt/actions/workflows/ci_tests_legacy.yaml) - [ ] All tests pass in the ["GMT Dev Tests" workflow](https://github.com/GenericMappingTools/pygmt/actions/workflows/ci_tests_dev.yaml) - [ ] All tests pass in the ["Doctests" workflow](https://github.com/GenericMappingTools/pygmt/actions/workflows/ci_doctests.yaml) -- [ ] Update warnings in `pygmt/_show_versions.py` as well as notes in - [Not working transparency](https://www.pygmt.org/dev/install.html#not-working-transparency) - regarding GMT-Ghostscript incompatibility +- [ ] Update warnings in `pygmt/_show_versions.py` as well as notes in [Not working transparency](https://www.pygmt.org/dev/install.html#not-working-transparency) regarding GMT-Ghostscript incompatibility - [ ] Reserve a DOI on [Zenodo](https://zenodo.org) by clicking on "New Version" - [ ] Finish up the "Changelog entry for v0.x.x" Pull Request (Use the previous changelog PR as a reference) - [ ] Run `make codespell` to check common misspellings. If there are any, either fix them or add them to `ignore-words-list` in `pyproject.toml` @@ -41,18 +39,16 @@ assignees: '' - [ ] Edit the draft release notes with the finalized changelog - [ ] Set the tag version and release title to vX.Y.Z - [ ] Make a release by clicking the 'Publish Release' button, this will automatically create a tag too -- [ ] Download pygmt-X.Y.Z.zip (rename to pygmt-vX.Y.Z.zip) and baseline-images.zip from - the release page, and upload the two zip files to https://zenodo.org/deposit, - ensure that they are filed under the correct reserved DOI +- [ ] Download pygmt-X.Y.Z.zip (rename to pygmt-vX.Y.Z.zip) and baseline-images.zip from the release page, and upload the two zip files to https://zenodo.org/deposit, ensure that they are filed under the correct reserved DOI **After release**: -- [ ] Update conda-forge [pygmt-feedstock](https://github.com/conda-forge/pygmt-feedstock) - [Done automatically by conda-forge's bot. Remember to pin Python and SPEC0 versions] +- [ ] Update conda-forge [pygmt-feedstock](https://github.com/conda-forge/pygmt-feedstock) [Done automatically by conda-forge's bot. Remember to pin GMT, Python and SPEC0 versions] - [ ] Bump PyGMT version on https://github.com/GenericMappingTools/try-gmt (after conda-forge update) - [ ] Announce the release on: - [ ] GMT [forum](https://forum.generic-mapping-tools.org/c/news/) (do this announcement first! Requires moderator status) - - [ ] [ResearchGate](https://www.researchgate.net) (after forum announcement, add new version as research item via the **code** category, be sure to include the corresponding new Zenodo DOI) + - [ ] [ResearchGate](https://www.researchgate.net) (after forum announcement; download the ZIP file of the new release from the release page and add it as research item via the **code** category, be sure to include the corresponding new Zenodo DOI) +- [ ] Update release checklist template with any additional bullet points that may have arisen during the release --- From e0f8e84b623149dac9a0bfe2cbf62b72cd4a4941 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 7 Jan 2025 07:53:54 +0800 Subject: [PATCH 09/35] Figure.plot/Figure.plot3d: Improve the docstrings for straight_line (#3720) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Yvonne Fröhlich <94163266+yvonnefroehlich@users.noreply.github.com> --- pygmt/src/plot.py | 49 +++++++++++++++++++++++++++++++++------------ pygmt/src/plot3d.py | 42 +++++++++++++++++++++++++++----------- 2 files changed, 66 insertions(+), 25 deletions(-) diff --git a/pygmt/src/plot.py b/pygmt/src/plot.py index ebfb2f22f3c..23c5bde12fd 100644 --- a/pygmt/src/plot.py +++ b/pygmt/src/plot.py @@ -2,6 +2,8 @@ plot - Plot in two dimensions. """ +from typing import Literal + from pygmt.clib import Session from pygmt.exceptions import GMTInvalidInput from pygmt.helpers import ( @@ -49,7 +51,15 @@ ) @kwargs_to_strings(R="sequence", c="sequence_comma", i="sequence_comma", p="sequence") def plot( - self, data=None, x=None, y=None, size=None, symbol=None, direction=None, **kwargs + self, + data=None, + x=None, + y=None, + size=None, + symbol=None, + direction=None, + straight_line: bool | Literal["x", "y"] = False, # noqa: ARG001 + **kwargs, ): r""" Plot lines, polygons, and symbols in 2-D. @@ -98,18 +108,29 @@ def plot( depending on the style options chosen. {projection} {region} - straight_line : bool or str - [**m**\|\ **p**\|\ **x**\|\ **y**]. - By default, geographic line segments are drawn as great circle - arcs. To draw them as straight lines, use - ``straight_line=True``. - Alternatively, add **m** to draw the line by first following a - meridian, then a parallel. Or append **p** to start following a - parallel, then a meridian. (This can be practical to draw a line - along parallels, for example). For Cartesian data, points are - simply connected, unless you append **x** or **y** to draw - stair-case curves that whose first move is along *x* or *y*, - respectively. + straight_line + By default, line segments are drawn as straight lines in the Cartesian and polar + coordinate systems, and as great circle arcs (by resampling coarse input data + along such arcs) in the geographic coordinate system. The ``straight_line`` + parameter can control the drawing of line segments. Valid values are: + + - ``True``: Draw line segments as straight lines in geographic coordinate + systems. + - ``"x"``: Draw line segments by first along *x*, then along *y*. + - ``"y"``: Draw line segments by first along *y*, then along *x*. + + Here, *x* and *y* have different meanings depending on the coordinate system: + + - **Cartesian** coordinate system: *x* and *y* are the X- and Y-axes. + - **Polar** coordinate system: *x* and *y* are theta and radius. + - **Geographic** coordinate system: *x* and *y* are parallels and meridians. + + .. attention:: + + There exits a bug in GMT<=6.5.0 that, in geographic coordinate systems, the + meaning of *x* and *y* is reversed, i.e., *x* means meridians and *y* means + parallels. The bug is fixed by upstream + `PR #8648 `__. {frame} {cmap} offset : str @@ -206,6 +227,8 @@ def plot( ``x``/``y``. {wrap} """ + # TODO(GMT>6.5.0): Remove the note for the upstream bug of the "straight_line" + # parameter. kwargs = self._preprocess(**kwargs) kind = data_kind(data) diff --git a/pygmt/src/plot3d.py b/pygmt/src/plot3d.py index f7f2b08290a..e8e75382d74 100644 --- a/pygmt/src/plot3d.py +++ b/pygmt/src/plot3d.py @@ -2,6 +2,8 @@ plot3d - Plot in three dimensions. """ +from typing import Literal + from pygmt.clib import Session from pygmt.exceptions import GMTInvalidInput from pygmt.helpers import ( @@ -58,6 +60,7 @@ def plot3d( size=None, symbol=None, direction=None, + straight_line: bool | Literal["x", "y"] = False, # noqa: ARG001 **kwargs, ): r""" @@ -108,18 +111,31 @@ def plot3d( zscale/zsize : float or str Set z-axis scaling or z-axis size. {region} - straight_line : bool or str - [**m**\|\ **p**\|\ **x**\|\ **y**]. - By default, geographic line segments are drawn as great circle - arcs. To draw them as straight lines, use ``straight_line``. - Alternatively, add **m** to draw the line by first following a - meridian, then a parallel. Or append **p** to start following a - parallel, then a meridian. (This can be practical to draw a line - along parallels, for example). For Cartesian data, points are - simply connected, unless you append **x** or **y** to draw - stair-case curves that whose first move is along *x* or *y*, - respectively. **Note**: The ``straight_line`` parameter requires - constant *z*-coordinates. + straight_line + By default, line segments are drawn as straight lines in the Cartesian and polar + coordinate systems, and as great circle arcs (by resampling coarse input data + along such arcs) in the geographic coordinate system. The ``straight_line`` + parameter can control the drawing of line segments. Valid values are: + + - ``True``: Draw line segments as straight lines in geographic coordinate + systems. + - ``"x"``: Draw line segments by first along *x*, then along *y*. + - ``"y"``: Draw line segments by first along *y*, then along *x*. + + Here, *x* and *y* have different meanings depending on the coordinate system: + + - **Cartesian** coordinate system: *x* and *y* are the X- and Y-axes. + - **Polar** coordinate system: *x* and *y* are theta and radius. + - **Geographic** coordinate system: *x* and *y* are parallels and meridians. + + **NOTE**: The ``straight_line`` parameter requires constant *z*-coordinates. + + .. attention:: + + There exits a bug in GMT<=6.5.0 that, in geographic coordinate systems, the + meaning of *x* and *y* is reversed, i.e., *x* means meridians and *y* means + parallels. The bug is fixed by upstream + `PR #8648 `__. {frame} {cmap} offset : str @@ -189,6 +205,8 @@ def plot3d( ``x``/``y``/``z``. {wrap} """ + # TODO(GMT>6.5.0): Remove the note for the upstream bug of the "straight_line" + # parameter. kwargs = self._preprocess(**kwargs) kind = data_kind(data) From 448e44c2a7f6d78202666488ee58f8c839a82930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yvonne=20Fr=C3=B6hlich?= <94163266+yvonnefroehlich@users.noreply.github.com> Date: Tue, 7 Jan 2025 09:03:49 +0100 Subject: [PATCH 10/35] DOC/Gallery example "Choropleth map": Fix typo (#3751) --- examples/gallery/maps/choropleth_map.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gallery/maps/choropleth_map.py b/examples/gallery/maps/choropleth_map.py index 19376f3c61c..b4b3bf54e69 100644 --- a/examples/gallery/maps/choropleth_map.py +++ b/examples/gallery/maps/choropleth_map.py @@ -6,7 +6,7 @@ polygons which are stored in a :class:`geopandas.GeoDataFrame` object. Use :func:`geopandas.read_file` to load data from any supported OGR format such as a shapefile (.shp), GeoJSON (.geojson), geopackage (.gpkg), etc. You can also use a full -URL pointing to your desired data source. Then, pass the class:`geopandas.GeoDataFrame` +URL pointing to your desired data source. Then, pass the :class:`geopandas.GeoDataFrame` as an argument to the ``data`` parameter of :meth:`pygmt.Figure.plot`, and style the geometry using the ``pen`` parameter. To fill the polygons based on a corresponding column you need to set ``fill="+z"`` as well as select the appropriate column using the From aeaf33fc352561cdb4881804aa0ec01efd9e91cf Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 8 Jan 2025 08:35:25 +0800 Subject: [PATCH 11/35] _load_remote_dataset: Add the "kind" attribute to explicitly specify if data is a grid or image (#3688) --- pygmt/datasets/load_remote_dataset.py | 36 +++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/pygmt/datasets/load_remote_dataset.py b/pygmt/datasets/load_remote_dataset.py index b19c055425c..41fe729d0e0 100644 --- a/pygmt/datasets/load_remote_dataset.py +++ b/pygmt/datasets/load_remote_dataset.py @@ -39,7 +39,9 @@ class GMTRemoteDataset(NamedTuple): Attributes ---------- description - The name assigned as an attribute to the DataArray. + The name assigned as an attribute to the DataArray. + kind + The kind of the dataset source. Valid values are ``"grid"`` and ``"image"``. units The units of the values in the DataArray. resolutions @@ -49,6 +51,7 @@ class GMTRemoteDataset(NamedTuple): """ description: str + kind: Literal["grid", "image"] units: str | None resolutions: dict[str, Resolution] extra_attributes: dict[str, Any] @@ -57,6 +60,7 @@ class GMTRemoteDataset(NamedTuple): datasets = { "earth_age": GMTRemoteDataset( description="EarthByte Earth seafloor crustal age", + kind="grid", units="Myr", extra_attributes={"horizontal_datum": "WGS84"}, resolutions={ @@ -75,6 +79,7 @@ class GMTRemoteDataset(NamedTuple): ), "earth_day": GMTRemoteDataset( description="NASA Day Images", + kind="image", units=None, extra_attributes={"long_name": "blue_marble", "horizontal_datum": "WGS84"}, resolutions={ @@ -94,6 +99,7 @@ class GMTRemoteDataset(NamedTuple): ), "earth_dist": GMTRemoteDataset( description="GSHHG Earth distance to shoreline", + kind="grid", units="kilometers", extra_attributes={"horizontal_datum": "WGS84"}, resolutions={ @@ -112,6 +118,7 @@ class GMTRemoteDataset(NamedTuple): ), "earth_edefl": GMTRemoteDataset( description="IGPP Earth east-west deflection", + kind="grid", units="micro-radians", extra_attributes={"horizontal_datum": "WGS84"}, resolutions={ @@ -130,6 +137,7 @@ class GMTRemoteDataset(NamedTuple): ), "earth_faa": GMTRemoteDataset( description="IGPP Earth free-air anomaly", + kind="grid", units="mGal", extra_attributes={"horizontal_datum": "WGS84"}, resolutions={ @@ -148,6 +156,7 @@ class GMTRemoteDataset(NamedTuple): ), "earth_faaerror": GMTRemoteDataset( description="IGPP Earth free-air anomaly errors", + kind="grid", units="mGal", extra_attributes={"horizontal_datum": "WGS84"}, resolutions={ @@ -166,6 +175,7 @@ class GMTRemoteDataset(NamedTuple): ), "earth_gebco": GMTRemoteDataset( description="GEBCO Earth relief", + kind="grid", units="meters", extra_attributes={"vertical_datum": "EGM96", "horizontal_datum": "WGS84"}, resolutions={ @@ -188,6 +198,7 @@ class GMTRemoteDataset(NamedTuple): ), "earth_geoid": GMTRemoteDataset( description="EGM2008 Earth geoid", + kind="grid", units="meters", extra_attributes={"horizontal_datum": "WGS84"}, resolutions={ @@ -206,6 +217,7 @@ class GMTRemoteDataset(NamedTuple): ), "earth_igpp": GMTRemoteDataset( description="IGPP Earth relief", + kind="grid", units="meters", extra_attributes={"vertical_datum": "EGM96", "horizontal_datum": "WGS84"}, resolutions={ @@ -228,6 +240,7 @@ class GMTRemoteDataset(NamedTuple): ), "earth_mag": GMTRemoteDataset( description="EMAG2 Earth Magnetic Anomaly Model", + kind="grid", units="nT", extra_attributes={"horizontal_datum": "WGS84"}, resolutions={ @@ -245,6 +258,7 @@ class GMTRemoteDataset(NamedTuple): ), "earth_mask": GMTRemoteDataset( description="GSHHG Earth mask", + kind="grid", units=None, extra_attributes={"horizontal_datum": "WGS84"}, resolutions={ @@ -265,6 +279,7 @@ class GMTRemoteDataset(NamedTuple): ), "earth_mss": GMTRemoteDataset( description="CNES Earth mean sea surface", + kind="grid", units="meters", extra_attributes={"horizontal_datum": "WGS84"}, resolutions={ @@ -283,6 +298,7 @@ class GMTRemoteDataset(NamedTuple): ), "earth_night": GMTRemoteDataset( description="NASA Night Images", + kind="image", units=None, extra_attributes={"long_name": "black_marble", "horizontal_datum": "WGS84"}, resolutions={ @@ -302,6 +318,7 @@ class GMTRemoteDataset(NamedTuple): ), "earth_mdt": GMTRemoteDataset( description="CNES Earth mean dynamic topography", + kind="grid", units="meters", extra_attributes={"horizontal_datum": "WGS84"}, resolutions={ @@ -315,6 +332,7 @@ class GMTRemoteDataset(NamedTuple): ), "earth_ndefl": GMTRemoteDataset( description="IGPP Earth north-south deflection", + kind="grid", units="micro-radians", extra_attributes={"horizontal_datum": "WGS84"}, resolutions={ @@ -333,6 +351,7 @@ class GMTRemoteDataset(NamedTuple): ), "earth_vgg": GMTRemoteDataset( description="IGPP Earth vertical gravity gradient", + kind="grid", units="Eotvos", extra_attributes={"horizontal_datum": "WGS84"}, resolutions={ @@ -351,6 +370,7 @@ class GMTRemoteDataset(NamedTuple): ), "earth_wdmam": GMTRemoteDataset( description="WDMAM World Digital Magnetic Anomaly Map", + kind="grid", units="nT", extra_attributes={"horizontal_datum": "WGS84"}, resolutions={ @@ -367,6 +387,7 @@ class GMTRemoteDataset(NamedTuple): ), "mars_relief": GMTRemoteDataset( description="NASA Mars (MOLA) relief", + kind="grid", units="meters", extra_attributes={}, resolutions={ @@ -388,6 +409,7 @@ class GMTRemoteDataset(NamedTuple): ), "moon_relief": GMTRemoteDataset( description="USGS Moon (LOLA) relief", + kind="grid", units="meters", extra_attributes={}, resolutions={ @@ -409,6 +431,7 @@ class GMTRemoteDataset(NamedTuple): ), "mercury_relief": GMTRemoteDataset( description="USGS Mercury relief", + kind="grid", units="meters", extra_attributes={}, resolutions={ @@ -428,6 +451,7 @@ class GMTRemoteDataset(NamedTuple): ), "pluto_relief": GMTRemoteDataset( description="USGS Pluto relief", + kind="grid", units="meters", extra_attributes={}, resolutions={ @@ -447,6 +471,7 @@ class GMTRemoteDataset(NamedTuple): ), "venus_relief": GMTRemoteDataset( description="NASA Magellan Venus relief", + kind="grid", units="meters", extra_attributes={}, resolutions={ @@ -545,15 +570,16 @@ def _load_remote_dataset( raise GMTInvalidInput(msg) fname = f"@{prefix}_{resolution}_{reg}" - kind = "image" if name in {"earth_day", "earth_night"} else "grid" - kwdict = {"R": region, "T": {"grid": "g", "image": "i"}[kind]} + kwdict = {"R": region, "T": {"grid": "g", "image": "i"}[dataset.kind]} with Session() as lib: - with lib.virtualfile_out(kind=kind) as voutgrd: + with lib.virtualfile_out(kind=dataset.kind) as voutgrd: lib.call_module( module="read", args=[fname, voutgrd, *build_arg_list(kwdict)], ) - grid = lib.virtualfile_to_raster(kind=kind, outgrid=None, vfname=voutgrd) + grid = lib.virtualfile_to_raster( + kind=dataset.kind, outgrid=None, vfname=voutgrd + ) # Full path to the grid if not tiled grids. source = which(fname, download="a") if not resinfo.tiled else None From a9d26a8248b2579f0f1a889d6d3258137fe7c0e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 08:36:33 +0800 Subject: [PATCH 12/35] Build(deps): Bump lycheeverse/lychee-action from 2.1.0 to 2.2.0 (#3753) Bumps [lycheeverse/lychee-action](https://github.com/lycheeverse/lychee-action) from 2.1.0 to 2.2.0. - [Release notes](https://github.com/lycheeverse/lychee-action/releases) - [Commits](https://github.com/lycheeverse/lychee-action/compare/v2.1.0...v2.2.0) --- updated-dependencies: - dependency-name: lycheeverse/lychee-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-links.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml index b1f45b73df8..0ed425da8d6 100644 --- a/.github/workflows/check-links.yml +++ b/.github/workflows/check-links.yml @@ -35,7 +35,7 @@ jobs: - name: Link Checker id: lychee - uses: lycheeverse/lychee-action@v2.1.0 + uses: lycheeverse/lychee-action@v2.2.0 with: fail: false # Don't fail action on broken links output: /tmp/lychee-out.md From 1d50ffb9f62f97fa861755e7b937c3d363bf4511 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 8 Jan 2025 21:24:38 +0800 Subject: [PATCH 13/35] TYP: Add return type hints for functions that return None (#3754) --- pygmt/_show_versions.py | 2 +- pygmt/clib/session.py | 18 ++++++++++++------ pygmt/figure.py | 12 ++++++------ pygmt/helpers/caching.py | 2 +- pygmt/helpers/tempfile.py | 2 +- pygmt/helpers/utils.py | 4 ++-- pygmt/session_management.py | 4 ++-- 7 files changed, 25 insertions(+), 19 deletions(-) diff --git a/pygmt/_show_versions.py b/pygmt/_show_versions.py index e529f053e46..d15bf0799c6 100644 --- a/pygmt/_show_versions.py +++ b/pygmt/_show_versions.py @@ -85,7 +85,7 @@ def _check_ghostscript_version(gs_version: str | None) -> str | None: return None -def show_versions(file: TextIO | None = sys.stdout): +def show_versions(file: TextIO | None = sys.stdout) -> None: """ Print various dependency versions which are useful when submitting bug reports. diff --git a/pygmt/clib/session.py b/pygmt/clib/session.py index f35161b7b02..179737d35f9 100644 --- a/pygmt/clib/session.py +++ b/pygmt/clib/session.py @@ -327,7 +327,7 @@ def get_libgmt_func( function.restype = restype return function - def create(self, name: str): + def create(self, name: str) -> None: """ Create a new GMT C API session. @@ -594,7 +594,7 @@ def get_common(self, option: str) -> bool | int | float | np.ndarray: case _: # 'status' is the option value (in integer type). return status - def call_module(self, module: str, args: str | list[str]): + def call_module(self, module: str, args: str | list[str]) -> None: """ Call a GMT module with the given arguments. @@ -946,7 +946,9 @@ def _check_dtype_and_dim(self, array: np.ndarray, ndim: int) -> int: raise GMTInvalidInput(msg) return self[DTYPES[dtype]] - def put_vector(self, dataset: ctp.c_void_p, column: int, vector: np.ndarray): + def put_vector( + self, dataset: ctp.c_void_p, column: int, vector: np.ndarray + ) -> None: r""" Attach a 1-D numpy array as a column on a GMT dataset. @@ -1005,7 +1007,9 @@ def put_vector(self, dataset: ctp.c_void_p, column: int, vector: np.ndarray): ) raise GMTCLibError(msg) - def put_strings(self, dataset: ctp.c_void_p, family: str, strings: np.ndarray): + def put_strings( + self, dataset: ctp.c_void_p, family: str, strings: np.ndarray + ) -> None: """ Attach a 1-D numpy array of dtype str as a column on a GMT dataset. @@ -1059,7 +1063,9 @@ def put_strings(self, dataset: ctp.c_void_p, family: str, strings: np.ndarray): msg = f"Failed to put strings of type {strings.dtype} into dataset." raise GMTCLibError(msg) - def put_matrix(self, dataset: ctp.c_void_p, matrix: np.ndarray, pad: int = 0): + def put_matrix( + self, dataset: ctp.c_void_p, matrix: np.ndarray, pad: int = 0 + ) -> None: """ Attach a 2-D numpy array to a GMT dataset. @@ -1204,7 +1210,7 @@ def read_data( raise GMTCLibError(msg) return ctp.cast(data_ptr, ctp.POINTER(dtype)) - def write_data(self, family, geometry, mode, wesn, output, data): + def write_data(self, family, geometry, mode, wesn, output, data) -> None: """ Write a GMT data container to a file. diff --git a/pygmt/figure.py b/pygmt/figure.py index 374eb1d8fee..5c5d4734ce6 100644 --- a/pygmt/figure.py +++ b/pygmt/figure.py @@ -95,19 +95,19 @@ class Figure: 122.94, 145.82, 20.53, 45.52 """ - def __init__(self): + def __init__(self) -> None: self._name = unique_name() self._preview_dir = TemporaryDirectory(prefix=f"{self._name}-preview-") self._activate_figure() - def __del__(self): + def __del__(self) -> None: """ Clean up the temporary directory that stores the previews. """ if hasattr(self, "_preview_dir"): self._preview_dir.cleanup() - def _activate_figure(self): + def _activate_figure(self) -> None: """ Start and/or activate the current figure. @@ -144,7 +144,7 @@ def savefig( show: bool = False, worldfile: bool = False, **kwargs, - ): + ) -> None: """ Save the figure to an image file. @@ -268,7 +268,7 @@ def show( width: int = 500, waiting: float = 0.5, **kwargs, - ): + ) -> None: """ Display a preview of the figure. @@ -442,7 +442,7 @@ def _repr_html_(self) -> str: ) -def set_display(method: Literal["external", "notebook", "none", None] = None): +def set_display(method: Literal["external", "notebook", "none", None] = None) -> None: """ Set the display method when calling :meth:`pygmt.Figure.show`. diff --git a/pygmt/helpers/caching.py b/pygmt/helpers/caching.py index 19d3eae0559..ea6bed8d4cf 100644 --- a/pygmt/helpers/caching.py +++ b/pygmt/helpers/caching.py @@ -5,7 +5,7 @@ from pygmt.src import which -def cache_data(): +def cache_data() -> None: """ Download GMT remote data files used in PyGMT tests and docs to cache folder. """ diff --git a/pygmt/helpers/tempfile.py b/pygmt/helpers/tempfile.py index 6d8dfd74fec..6995be1db98 100644 --- a/pygmt/helpers/tempfile.py +++ b/pygmt/helpers/tempfile.py @@ -59,7 +59,7 @@ class GMTTempFile: [0. 0. 0.] [1. 1. 1.] [2. 2. 2.] """ - def __init__(self, prefix: str = "pygmt-", suffix: str = ".txt"): + def __init__(self, prefix: str = "pygmt-", suffix: str = ".txt") -> None: """ Initialize the object. """ diff --git a/pygmt/helpers/utils.py b/pygmt/helpers/utils.py index 32fad37e4ff..b53942818c5 100644 --- a/pygmt/helpers/utils.py +++ b/pygmt/helpers/utils.py @@ -43,7 +43,7 @@ def _validate_data_input( data=None, x=None, y=None, z=None, required_z=False, required_data=True, kind=None -): +) -> None: """ Check if the combination of data/x/y/z is valid. @@ -552,7 +552,7 @@ def is_nonstr_iter(value): return isinstance(value, Iterable) and not isinstance(value, str) -def launch_external_viewer(fname: str, waiting: float = 0): +def launch_external_viewer(fname: str, waiting: float = 0) -> None: """ Open a file in an external viewer program. diff --git a/pygmt/session_management.py b/pygmt/session_management.py index be3cff9539c..ac18218c858 100644 --- a/pygmt/session_management.py +++ b/pygmt/session_management.py @@ -9,7 +9,7 @@ from pygmt.helpers import unique_name -def begin(): +def begin() -> None: """ Initiate a new GMT modern mode session. @@ -28,7 +28,7 @@ def begin(): lib.call_module(module="set", args=["GMT_COMPATIBILITY=6"]) -def end(): +def end() -> None: """ Terminate the GMT modern mode session created by :func:`pygmt.begin`. From 0ba87ba369466de605c39a020d7564be1bc3ef1f Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 8 Jan 2025 21:33:21 +0800 Subject: [PATCH 14/35] CI: Update the doc deploy script to generate CNAME and index.html (#3749) --- .github/workflows/ci_docs.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index a74d4fbfcd5..06c7debb426 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -163,9 +163,14 @@ jobs: # to get the right commit hash. message="Deploy $version from $(git rev-parse --short HEAD)" cd deploy - # Need to have this file so that GitHub doesn't try to run Jekyll + # Create some files in the root directory. + # .nojekyll: Need to have this file so that GitHub doesn't try to run Jekyll touch .nojekyll - # Delete all the files and replace with our new set + # CNAME: Set the custom domain name + echo "www.pygmt.org" > CNAME + # index.html: Redirect to the latest version + echo '' > index.html + # Delete all the files and replace with our new set echo -e "\nRemoving old files from previous builds of ${version}:" rm -rvf ${version} echo -e "\nCopying HTML files to ${version}:" From 664a9eda6fa03adc24b9a1752c601c438528e5e3 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 8 Jan 2025 21:43:31 +0800 Subject: [PATCH 15/35] ruff: Ignore the B018 rule (useless expressions) in examples (#3750) --- examples/gallery/images/rgb_image.py | 2 +- examples/gallery/lines/linestrings.py | 2 +- examples/gallery/maps/choropleth_map.py | 2 +- examples/tutorials/basics/plot.py | 6 +++--- pyproject.toml | 5 ++++- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/examples/gallery/images/rgb_image.py b/examples/gallery/images/rgb_image.py index 56d7b9d2f70..8afb2c93e25 100644 --- a/examples/gallery/images/rgb_image.py +++ b/examples/gallery/images/rgb_image.py @@ -28,7 +28,7 @@ # Subset to area of Lāhainā in EPSG:32604 coordinates image = img.rio.clip_box(minx=738000, maxx=755000, miny=2300000, maxy=2318000) image = image.load() # Force loading the DataArray into memory -image # noqa: B018 +image # %% # Plot the RGB imagery: diff --git a/examples/gallery/lines/linestrings.py b/examples/gallery/lines/linestrings.py index d3793645c84..18f94502f16 100644 --- a/examples/gallery/lines/linestrings.py +++ b/examples/gallery/lines/linestrings.py @@ -25,7 +25,7 @@ # Convert object to EPSG 4326 coordinate system gdf = gdf.to_crs("EPSG:4326") -print(gdf.head()) +gdf.head() # %% fig = pygmt.Figure() diff --git a/examples/gallery/maps/choropleth_map.py b/examples/gallery/maps/choropleth_map.py index b4b3bf54e69..f1cce8c3014 100644 --- a/examples/gallery/maps/choropleth_map.py +++ b/examples/gallery/maps/choropleth_map.py @@ -20,7 +20,7 @@ # Read the example dataset provided by geodatasets. gdf = gpd.read_file(geodatasets.get_path("geoda airbnb")) -print(gdf) +print(gdf.head()) # %% fig = pygmt.Figure() diff --git a/examples/tutorials/basics/plot.py b/examples/tutorials/basics/plot.py index babfb60d771..42cb8aad5ae 100644 --- a/examples/tutorials/basics/plot.py +++ b/examples/tutorials/basics/plot.py @@ -18,7 +18,9 @@ # The data are loaded as a :class:`pandas.DataFrame`. data = pygmt.datasets.load_sample_data(name="japan_quakes") +data.head() +# %% # Set the region for the plot to be slightly larger than the data bounds. region = [ data.longitude.min() - 1, @@ -26,9 +28,7 @@ data.latitude.min() - 1, data.latitude.max() + 1, ] - -print(region) -print(data.head()) +region # %% # We'll use the :meth:`pygmt.Figure.plot` method to plot circles on the diff --git a/pyproject.toml b/pyproject.toml index 9af670df32c..61c6a541fef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -160,7 +160,10 @@ known-third-party = ["pygmt"] [tool.ruff.lint.per-file-ignores] "__init__.py" = ["F401"] # Ignore `F401` (unused-import) in all `__init__.py` files "*/tests/test_*.py" = ["S101"] # Ignore `S101` (use of assert) in all tests files -"examples/**/*.py" = ["T201"] # Allow `print` in examples +"examples/**/*.py" = [ # Ignore rules in examples + "B018", # Allow useless expressions in Jupyter Notebooks + "T201", # Allow `print` statements +] [tool.ruff.lint.pycodestyle] max-doc-length = 88 From c7198e6f2d38edca746b87886f2e7c0933d3f2c9 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 9 Jan 2025 08:20:52 +0800 Subject: [PATCH 16/35] DOC: Make bullet point lists consistent in docstrings (":" and lower-case) Part II (#3752) --- pygmt/helpers/decorators.py | 70 ++++++++++++++++++------------------- pygmt/src/grdfilter.py | 14 ++++---- pygmt/src/grdgradient.py | 14 ++++---- pygmt/src/grdview.py | 10 +++--- pygmt/src/sphdistance.py | 6 ++-- pygmt/src/x2sys_cross.py | 6 ++-- pygmt/src/x2sys_init.py | 14 ++++---- 7 files changed, 67 insertions(+), 67 deletions(-) diff --git a/pygmt/helpers/decorators.py b/pygmt/helpers/decorators.py index a22d49334af..08822d42a86 100644 --- a/pygmt/helpers/decorators.py +++ b/pygmt/helpers/decorators.py @@ -37,17 +37,17 @@ (using ``binary="o"``), where *ncols* is the number of data columns of *type*, which must be one of: - - **c** - int8_t (1-byte signed char) - - **u** - uint8_t (1-byte unsigned char) - - **h** - int16_t (2-byte signed int) - - **H** - uint16_t (2-byte unsigned int) - - **i** - int32_t (4-byte signed int) - - **I** - uint32_t (4-byte unsigned int) - - **l** - int64_t (8-byte signed int) - - **L** - uint64_t (8-byte unsigned int) - - **f** - 4-byte single-precision float - - **d** - 8-byte double-precision float - - **x** - use to skip *ncols* anywhere in the record + - **c**: int8_t (1-byte signed char) + - **u**: uint8_t (1-byte unsigned char) + - **h**: int16_t (2-byte signed int) + - **H**: uint16_t (2-byte unsigned int) + - **i**: int32_t (4-byte signed int) + - **I**: uint32_t (4-byte unsigned int) + - **l**: int64_t (8-byte signed int) + - **L**: uint64_t (8-byte unsigned int) + - **f**: 4-byte single-precision float + - **d**: 8-byte double-precision float + - **x**: use to skip *ncols* anywhere in the record For records with mixed types, append additional comma-separated combinations of *ncols* *type* (no space). The following modifiers @@ -84,9 +84,9 @@ **e**\|\ **f**\|\ **g**. Determine how spherical distances are calculated. - - **e** - Ellipsoidal (or geodesic) mode - - **f** - Flat Earth mode - - **g** - Great circle distance [Default] + - **e**: Ellipsoidal (or geodesic) mode + - **f**: Flat Earth mode + - **g**: Great circle distance [Default] All spherical distance calculations depend on the current ellipsoid (:gmt-term:`PROJ_ELLIPSOID`), the definition of the mean radius @@ -118,16 +118,16 @@ a list with each item containing a string describing one set of criteria. - - **x**\|\ **X** - define a gap when there is a large enough + - **x**\|\ **X**: define a gap when there is a large enough change in the x coordinates (upper case to use projected coordinates). - - **y**\|\ **Y** - define a gap when there is a large enough + - **y**\|\ **Y**: define a gap when there is a large enough change in the y coordinates (upper case to use projected coordinates). - - **d**\|\ **D** - define a gap when there is a large enough + - **d**\|\ **D**: define a gap when there is a large enough distance between coordinates (upper case to use projected coordinates). - - **z** - define a gap when there is a large enough change in + - **z**: define a gap when there is a large enough change in the z data. Use **+c**\ *col* to change the z data column [Default *col* is 2 (i.e., 3rd column)]. @@ -146,9 +146,9 @@ One of the following modifiers can be appended: - - **+n** - specify that the previous value minus the current + - **+n**: specify that the previous value minus the current column value must exceed *gap* for a break to be imposed. - - **+p** - specify that the current value minus the previous + - **+p**: specify that the current value minus the previous value must exceed *gap* for a break to be imposed.""", "grid": r""" grid : str or xarray.DataArray @@ -367,13 +367,13 @@ Select verbosity level [Default is **w**], which modulates the messages written to stderr. Choose among 7 levels of verbosity: - - **q** - Quiet, not even fatal error messages are produced - - **e** - Error messages only - - **w** - Warnings [Default] - - **t** - Timings (report runtimes for time-intensive algorithms) - - **i** - Informational messages (same as ``verbose=True``) - - **c** - Compatibility warnings - - **d** - Debugging messages""", + - **q**: Quiet, not even fatal error messages are produced + - **e**: Error messages only + - **w**: Warnings [Default] + - **t**: Timings (report runtimes for time-intensive algorithms) + - **i**: Informational messages (same as ``verbose=True``) + - **c**: Compatibility warnings + - **d**: Debugging messages""", "wrap": r""" wrap : str **y**\|\ **a**\|\ **w**\|\ **d**\|\ **h**\|\ **m**\|\ **s**\|\ @@ -382,14 +382,14 @@ different column if selected via **+c**\ *col*. The following cyclical coordinate transformations are supported: - - **y** - yearly cycle (normalized) - - **a** - annual cycle (monthly) - - **w** - weekly cycle (day) - - **d** - daily cycle (hour) - - **h** - hourly cycle (minute) - - **m** - minute cycle (second) - - **s** - second cycle (second) - - **c** - custom cycle (normalized) + - **y**: yearly cycle (normalized) + - **a**: annual cycle (monthly) + - **w**: weekly cycle (day) + - **d**: daily cycle (hour) + - **h**: hourly cycle (minute) + - **m**: minute cycle (second) + - **s**: second cycle (second) + - **c**: custom cycle (normalized) Full documentation is at :gmt-docs:`gmt.html#w-full`.""", } diff --git a/pygmt/src/grdfilter.py b/pygmt/src/grdfilter.py index 1ad044d0b5e..786e280dd61 100644 --- a/pygmt/src/grdfilter.py +++ b/pygmt/src/grdfilter.py @@ -48,13 +48,13 @@ def grdfilter(grid, outgrid: str | None = None, **kwargs) -> xr.DataArray | None [/*width2*\][*modifiers*]. Name of the filter type you wish to apply, followed by the *width*: - - **b** - Box Car - - **c** - Cosine Arch - - **g** - Gaussian - - **o** - Operator - - **m** - Median - - **p** - Maximum Likelihood probability - - **h** - Histogram + - **b**: Box Car + - **c**: Cosine Arch + - **g**: Gaussian + - **o**: Operator + - **m**: Median + - **p**: Maximum Likelihood probability + - **h**: Histogram distance : str State how the grid (x,y) relates to the filter *width*: diff --git a/pygmt/src/grdgradient.py b/pygmt/src/grdgradient.py index a793be04df0..96c4c61937f 100644 --- a/pygmt/src/grdgradient.py +++ b/pygmt/src/grdgradient.py @@ -69,11 +69,11 @@ def grdgradient(grid, outgrid: str | None = None, **kwargs) -> xr.DataArray | No Find the direction of the positive (up-slope) gradient of the data. The following options are supported: - - **a** - Find the aspect (i.e., the down-slope direction) - - **c** - Use the conventional Cartesian angles measured + - **a**: Find the aspect (i.e., the down-slope direction) + - **c**: Use the conventional Cartesian angles measured counterclockwise from the positive x (east) direction. - - **o** - Report orientations (0-180) rather than directions (0-360). - - **n** - Add 90 degrees to all angles (e.g., to give local strikes of + - **o**: Report orientations (0-180) rather than directions (0-360). + - **n**: Add 90 degrees to all angles (e.g., to give local strikes of the surface). radiance : str or list [**m**\|\ **s**\|\ **p**]\ *azim/elev*\ [**+a**\ *ambient*][**+d**\ @@ -102,14 +102,14 @@ def grdgradient(grid, outgrid: str | None = None, **kwargs) -> xr.DataArray | No given, it is set to the average of :math:`g`. The following forms are supported: - - **True** - Normalize using :math:`g_n = \mbox{{amp}}\ + - **True**: Normalize using :math:`g_n = \mbox{{amp}}\ (\frac{{g - \mbox{{offset}}}}{{max(|g - \mbox{{offset}}|)}})` - - **e** - Normalize using a cumulative Laplace distribution yielding: + - **e**: Normalize using a cumulative Laplace distribution yielding: :math:`g_n = \mbox{{amp}}(1 - \ \exp{{(\sqrt{{2}}\frac{{g - \mbox{{offset}}}}{{\sigma}}))}}`, where :math:`\sigma` is estimated using the L1 norm of :math:`(g - \mbox{{offset}})` if it is not given. - - **t** - Normalize using a cumulative Cauchy distribution yielding: + - **t**: Normalize using a cumulative Cauchy distribution yielding: :math:`g_n = \ \frac{{2(\mbox{{amp}})}}{{\pi}}(\tan^{{-1}}(\frac{{g - \ \mbox{{offset}}}}{{\sigma}}))` where :math:`\sigma` is estimated diff --git a/pygmt/src/grdview.py b/pygmt/src/grdview.py index ba7341046e0..77ace573a30 100644 --- a/pygmt/src/grdview.py +++ b/pygmt/src/grdview.py @@ -76,12 +76,12 @@ def grdview(self, grid, **kwargs): Specify cover type of the grid. Select one of following settings: - - **m** - mesh plot [Default]. - - **mx** or **my** - waterfall plots (row or column profiles). - - **s** - surface plot, and optionally append **m** to have mesh lines + - **m**: mesh plot [Default]. + - **mx** or **my**: waterfall plots (row or column profiles). + - **s**: surface plot, and optionally append **m** to have mesh lines drawn on top of the surface. - - **i** - image plot. - - **c** - Same as **i** but will make nodes with z = NaN transparent. + - **i**: image plot. + - **c**: Same as **i** but will make nodes with z = NaN transparent. For any of these choices, you may force a monochrome image by appending the modifier **+m**. diff --git a/pygmt/src/sphdistance.py b/pygmt/src/sphdistance.py index ab49f961e03..279db4e3590 100644 --- a/pygmt/src/sphdistance.py +++ b/pygmt/src/sphdistance.py @@ -65,10 +65,10 @@ def sphdistance( Specify the quantity that should be assigned to the grid nodes [Default is **d**]: - - **d** - compute distances to the nearest data point - - **n** - assign the ID numbers of the Voronoi polygons that each + - **d**: compute distances to the nearest data point + - **n**: assign the ID numbers of the Voronoi polygons that each grid node is inside - - **z** - assign all nodes inside the polygon the z-value of the center + - **z**: assign all nodes inside the polygon the z-value of the center node for a natural nearest-neighbor grid. Optionally, append the resampling interval along Voronoi arcs in diff --git a/pygmt/src/x2sys_cross.py b/pygmt/src/x2sys_cross.py index f7d9f8e1843..4e209d33d18 100644 --- a/pygmt/src/x2sys_cross.py +++ b/pygmt/src/x2sys_cross.py @@ -145,9 +145,9 @@ def x2sys_cross( Sets the interpolation mode for estimating values at the crossover. Choose among: - - **l** - Linear interpolation [Default]. - - **a** - Akima spline interpolation. - - **c** - Cubic spline interpolation. + - **l**: Linear interpolation [Default]. + - **a**: Akima spline interpolation. + - **c**: Cubic spline interpolation. coe : str Use **e** for external COEs only, and **i** for internal COEs only diff --git a/pygmt/src/x2sys_init.py b/pygmt/src/x2sys_init.py index 99af7211424..6e36263fe0e 100644 --- a/pygmt/src/x2sys_init.py +++ b/pygmt/src/x2sys_init.py @@ -85,13 +85,13 @@ def x2sys_init(tag, **kwargs): programs. Append **d** for distance or **s** for speed, then give the desired *unit* as: - - **c** - Cartesian userdist or userdist/usertime - - **e** - meters or m/s - - **f** - feet or ft/s - - **k** - kilometers or km/hr - - **m** - miles or mi/hr - - **n** - nautical miles or knots - - **u** - survey feet or sft/s + - **c**: Cartesian userdist or userdist/usertime + - **e**: meters or m/s + - **f**: feet or ft/s + - **k**: kilometers or km/hr + - **m**: miles or mi/hr + - **n**: nautical miles or knots + - **u**: survey feet or sft/s [Default is ``units=["dk", "se"]`` (km and m/s) if ``discontinuity`` is set, and ``units=["dc", "sc"]`` otherwise (e.g., for Cartesian units)]. From d33e5ac5286e260db534c1c8925ee544ecfa4a9e Mon Sep 17 00:00:00 2001 From: Michael Grund <23025878+michaelgrund@users.noreply.github.com> Date: Thu, 9 Jan 2025 01:54:14 +0100 Subject: [PATCH 17/35] DOC: Make usage of "lowercase" and "uppercase" consistent across all docs (#3756) --- doc/techref/patterns.md | 2 +- examples/gallery/basemaps/double_y_axes.py | 2 +- examples/gallery/embellishments/scalebar.py | 5 +++-- examples/gallery/images/cross_section.py | 2 +- examples/gallery/lines/decorated_lines.py | 2 +- examples/gallery/lines/quoted_lines.py | 2 +- examples/gallery/symbols/multi_parameter_symbols.py | 2 +- examples/projections/cyl/cyl_oblique_mercator.py | 2 +- examples/projections/nongeo/cartesian_linear.py | 2 +- examples/projections/nongeo/cartesian_logarithmic.py | 2 +- examples/projections/nongeo/cartesian_power.py | 2 +- examples/projections/nongeo/polar.py | 2 +- examples/tutorials/basics/frames.py | 4 ++-- pygmt/helpers/decorators.py | 6 +++--- pygmt/src/filter1d.py | 2 +- pygmt/src/project.py | 2 +- pygmt/src/text.py | 8 ++++---- 17 files changed, 25 insertions(+), 24 deletions(-) diff --git a/doc/techref/patterns.md b/doc/techref/patterns.md index 17deb045aa3..de7df02d047 100644 --- a/doc/techref/patterns.md +++ b/doc/techref/patterns.md @@ -11,7 +11,7 @@ image raster file. The former will result in one of the 90 predefined 64x64 bit- provided by GMT (see the figure below). The latter allows the user to create customized, repeating images using image raster files. -By specifying upper case **P** instead of **p** the image will be bit-reversed, i.e., +By specifying uppercase **P** instead of **p** the image will be bit-reversed, i.e., white and black areas will be interchanged (only applies to 1-bit images or predefined bit-image patterns). For these patterns and other 1-bit images one may specify alternative **b**ackground and **f**oreground colors (by appending **+b**_color_ and/or diff --git a/examples/gallery/basemaps/double_y_axes.py b/examples/gallery/basemaps/double_y_axes.py index aa8ba8a6815..c6f970e59ca 100644 --- a/examples/gallery/basemaps/double_y_axes.py +++ b/examples/gallery/basemaps/double_y_axes.py @@ -5,7 +5,7 @@ The ``frame`` parameter of the plotting methods of the :class:`pygmt.Figure` class can control which axes should be plotted and optionally show annotations, tick marks, and gridlines. By default, all 4 axes are plotted, along with -annotations and tick marks (denoted **W**, **S**, **E**, **N**). Lower case +annotations and tick marks (denoted **W**, **S**, **E**, **N**). Lowercase versions (**w**, **s**, **e**, **n**) can be used to denote to only plot the axes with tick marks. We can also only plot the axes without annotations and tick marks using **l** (left axis), **r** (right axis), **t** (top axis), diff --git a/examples/gallery/embellishments/scalebar.py b/examples/gallery/embellishments/scalebar.py index bca7d0b9703..4b829165fe5 100644 --- a/examples/gallery/embellishments/scalebar.py +++ b/examples/gallery/embellishments/scalebar.py @@ -12,8 +12,9 @@ - **g**: Give map coordinates as *longitude*\/\ *latitude*. - **j**\|\ **J**: Specify a two-character (order independent) code. Choose from vertical **T**\(op), **M**\(iddle), or **B**\(ottom) and - horizontal **L**\(eft), **C**\(entre), or **R**\(ight). Lower / upper - case **j** / **J** mean inside / outside of the map bounding box. + horizontal **L**\(eft), **C**\(entre), or **R**\(ight). Lower / + uppercase **j** / **J** mean inside / outside of the map bounding + box. - **n**: Give normalized bounding box coordinates as *nx*\/\ *ny*. - **x**: Give plot coordinates as *x*\/\ *y*. diff --git a/examples/gallery/images/cross_section.py b/examples/gallery/images/cross_section.py index 0f0cd8e352e..d451c8c3ee8 100644 --- a/examples/gallery/images/cross_section.py +++ b/examples/gallery/images/cross_section.py @@ -37,7 +37,7 @@ # Add a colorbar for the elevation fig.colorbar( - # Place the colorbar inside the plot (lower-case "j") in the Bottom Right (BR) + # Place the colorbar inside the plot (lowercase "j") in the Bottom Right (BR) # corner with an offset ("+o") of 0.7 centimeters and 0.3 centimeters in x or y # directions, respectively; move the x label above the horizontal colorbar ("+ml") position="jBR+o0.7c/0.8c+h+w5c/0.3c+ml", diff --git a/examples/gallery/lines/decorated_lines.py b/examples/gallery/lines/decorated_lines.py index 42ad5dc7be9..cbe6b6b510d 100644 --- a/examples/gallery/lines/decorated_lines.py +++ b/examples/gallery/lines/decorated_lines.py @@ -51,7 +51,7 @@ "~d1c:+sd0.5c+gtan+p1p,black+n-0.2c/0.1c", # Give the number of equally spaced symbols by using "n" instead of "d" "~n6:+sn0.5c+gtan+p1p,black", - # Use upper-case "N" to have symbols at the start and end of the line + # Use uppercase "N" to have symbols at the start and end of the line "~N6:+sh0.5c+gtan+p1p,black", # Suppress the main decorated line by appending "+i" "~d1c:+sg0.5c+gtan+p1p,black+i", diff --git a/examples/gallery/lines/quoted_lines.py b/examples/gallery/lines/quoted_lines.py index 9e70ec15c4a..2ccfb1309a6 100644 --- a/examples/gallery/lines/quoted_lines.py +++ b/examples/gallery/lines/quoted_lines.py @@ -33,7 +33,7 @@ "qd1c:+ltext+i", # Give the number of equally spaced labels by using "n" instead of "d" "qn5:+ltext", - # Use upper-case "N" to have labels at the start and end of the line + # Use uppercase "N" to have labels at the start and end of the line "qN5:+ltext", # To only plot a label at the start of the line use "N-1" "qN-1:+ltext", diff --git a/examples/gallery/symbols/multi_parameter_symbols.py b/examples/gallery/symbols/multi_parameter_symbols.py index 6baa607f1c5..72480aba063 100644 --- a/examples/gallery/symbols/multi_parameter_symbols.py +++ b/examples/gallery/symbols/multi_parameter_symbols.py @@ -33,7 +33,7 @@ # directions given in degrees counter-clockwise from horizontal. Append **+i** and the # desired value to apply an inner diameter. # -# Upper-case versions **E**, **J**, and **W** are similar to **e**, **j**, and **w** +# Uppercase versions **E**, **J**, and **W** are similar to **e**, **j**, and **w** # but expect geographic azimuths and distances. fig = pygmt.Figure() diff --git a/examples/projections/cyl/cyl_oblique_mercator.py b/examples/projections/cyl/cyl_oblique_mercator.py index 17bf589d48b..22db4f973da 100644 --- a/examples/projections/cyl/cyl_oblique_mercator.py +++ b/examples/projections/cyl/cyl_oblique_mercator.py @@ -9,7 +9,7 @@ The projection is set with **o** or **O**. There are three different specification ways (**a**\|\ **A**, **b**\|\ **B**, **c**\|\ **C**) available. For all three -definitions, the upper case letter mean the projection pole is set in the southern +definitions, the uppercase letter mean the projection pole is set in the southern hemisphere [Default is northern hemisphere]. Align the y-axis with the optional modifier **+v**. The figure size is set with *scale* or *width*. """ diff --git a/examples/projections/nongeo/cartesian_linear.py b/examples/projections/nongeo/cartesian_linear.py index 021d080ae94..83e986f6739 100644 --- a/examples/projections/nongeo/cartesian_linear.py +++ b/examples/projections/nongeo/cartesian_linear.py @@ -4,7 +4,7 @@ **X**\ *width*\ [/*height*] or **x**\ *x-scale*\ [/*y-scale*] -Give the *width* of the figure and the optional *height*. The lower-case version +Give the *width* of the figure and the optional *height*. The lowercase version **x** is similar to **X** but expects an *x-scale* and an optional *y-scale*. The Cartesian linear projection is primarily designed for regular floating point diff --git a/examples/projections/nongeo/cartesian_logarithmic.py b/examples/projections/nongeo/cartesian_logarithmic.py index ef354dba73f..0e4619075ea 100644 --- a/examples/projections/nongeo/cartesian_logarithmic.py +++ b/examples/projections/nongeo/cartesian_logarithmic.py @@ -6,7 +6,7 @@ **x**\ *x-scale*\ [**l**][/*y-scale*\ [**l**]] Give the *width* of the figure and the optional *height*. -The lower-case version **x** is similar to **X** but expects +The lowercase version **x** is similar to **X** but expects an *x-scale* and an optional *y-scale*. Each axis with a logarithmic transformation requires **l** after its size argument. diff --git a/examples/projections/nongeo/cartesian_power.py b/examples/projections/nongeo/cartesian_power.py index 862ceba8595..b5f856713ed 100644 --- a/examples/projections/nongeo/cartesian_power.py +++ b/examples/projections/nongeo/cartesian_power.py @@ -6,7 +6,7 @@ **x**\ *x-scale*\ [**p**\ *pvalue*][/*y-scale*\ [**p**\ *pvalue*]] Give the *width* of the figure and the optional argument *height*. -The lower-case version **x** is similar to **X** but expects +The lowercase version **x** is similar to **X** but expects an *x-scale* and an optional *y-scale*. Each axis with a power transformation requires **p** and the exponent for that axis after its size argument. diff --git a/examples/projections/nongeo/polar.py b/examples/projections/nongeo/polar.py index 5c71e517c17..760838b34ac 100644 --- a/examples/projections/nongeo/polar.py +++ b/examples/projections/nongeo/polar.py @@ -12,7 +12,7 @@ Limits are set via the ``region`` parameter ([*theta_min*, *theta_max*, *radius_min*, *radius_max*]). When using **P**\ *width* you -have to give the *width* of the figure. The lower-case version **p** is similar to **P** +have to give the *width* of the figure. The lowercase version **p** is similar to **P** but expects a *scale* instead of a width (**p**\ *scale*). The following customizing modifiers are available: diff --git a/examples/tutorials/basics/frames.py b/examples/tutorials/basics/frames.py index 56b4e9ac130..b3fb466fa23 100644 --- a/examples/tutorials/basics/frames.py +++ b/examples/tutorials/basics/frames.py @@ -90,9 +90,9 @@ # :meth:`pygmt.Figure.basemap`. The map boundaries (or plot axes) are named as # West/west/left (**W**, **w**, **l**), South/south/bottom # (**S**, **s**, **b**), North/north/top (**N**, **n**, **t**), and -# East/east/right (**E**, **e**, **r**) sides of a figure. If an upper-case +# East/east/right (**E**, **e**, **r**) sides of a figure. If an uppercase # letter (**W**, **S**, **N**, **E**) is passed, the axis is plotted with -# tick marks and annotations. The lower-case version +# tick marks and annotations. The lowercase version # (**w**, **s**, **n**, **e**) plots the axis only with tick marks. # To only plot the axis pass **l**, **b**, **t**, **r**. By default # (``frame=True`` or ``frame="af"``), the West and the South axes are diff --git a/pygmt/helpers/decorators.py b/pygmt/helpers/decorators.py index 08822d42a86..3c4f9dd5510 100644 --- a/pygmt/helpers/decorators.py +++ b/pygmt/helpers/decorators.py @@ -119,13 +119,13 @@ criteria. - **x**\|\ **X**: define a gap when there is a large enough - change in the x coordinates (upper case to use projected + change in the x coordinates (uppercase to use projected coordinates). - **y**\|\ **Y**: define a gap when there is a large enough - change in the y coordinates (upper case to use projected + change in the y coordinates (uppercase to use projected coordinates). - **d**\|\ **D**: define a gap when there is a large enough - distance between coordinates (upper case to use projected + distance between coordinates (uppercase to use projected coordinates). - **z**: define a gap when there is a large enough change in the z data. Use **+c**\ *col* to change the z data column diff --git a/pygmt/src/filter1d.py b/pygmt/src/filter1d.py index 206c3a2fd48..6d0e97938cf 100644 --- a/pygmt/src/filter1d.py +++ b/pygmt/src/filter1d.py @@ -75,7 +75,7 @@ def filter1d( - **u**: upper (absolute). Return maximum of all values. - **U**: upper. Return maximum of all negative values only. - Upper case type **B**, **C**, **G**, **M**, **P**, **F** will use + Uppercase type **B**, **C**, **G**, **M**, **P**, **F** will use robust filter versions: i.e., replace outliers (2.5 L1 scale off median, using 1.4826 \* median absolute deviation [MAD]) with median during filtering. diff --git a/pygmt/src/project.py b/pygmt/src/project.py index f90e517b202..a49d5a1ad1f 100644 --- a/pygmt/src/project.py +++ b/pygmt/src/project.py @@ -136,7 +136,7 @@ def project( convention : str Specify the desired output using any combination of **xyzpqrs**, in any order [Default is **xypqrsz**]. Do not space between the letters. - Use lower case. The output will be columns of values corresponding to + Use lowercase. The output will be columns of values corresponding to your ``convention``. The **z** flag is special and refers to all numerical columns beyond the leading **x** and **y** in your input record. The **z** flag also includes any trailing text (which is diff --git a/pygmt/src/text.py b/pygmt/src/text.py index 75b2653043c..b507510f620 100644 --- a/pygmt/src/text.py +++ b/pygmt/src/text.py @@ -138,11 +138,11 @@ def text_( # noqa: PLR0912 **i** for inches, or **p** for points; if not given we consult :gmt-term:`PROJ_LENGTH_UNIT`) or *%* for a percentage of the font size. Optionally, use modifier **+t** to set the shape of the text - box when using ``fill`` and/or ``pen``. Append lower case **o** - to get a straight rectangle [Default is **o**]. Append upper case + box when using ``fill`` and/or ``pen``. Append lowercase **o** + to get a straight rectangle [Default is **o**]. Append uppercase **O** to get a rounded rectangle. In paragraph mode (*paragraph*) - you can also append lower case **c** to get a concave rectangle or - append upper case **C** to get a convex rectangle. + you can also append lowercase **c** to get a concave rectangle or + append uppercase **C** to get a convex rectangle. fill : str Set color for filling text boxes [Default is no fill]. offset : str From 0bf733f54ca6f2e387954be2cbc146f7ca59739d Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 9 Jan 2025 08:55:21 +0800 Subject: [PATCH 18/35] clib.conversion._to_numpy: Add tests for numpy array with np.datetime64 dtypes (#3687) --- pygmt/tests/test_clib_to_numpy.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index c3bc413a0b1..79858bc04d4 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -153,6 +153,36 @@ def test_to_numpy_numpy_string(dtype): npt.assert_array_equal(result, array) +@pytest.mark.parametrize( + "dtype", + [ + np.datetime64, # The expected dtype is "datetime64[D]" for this test. + "datetime64[Y]", + "datetime64[M]", + "datetime64[W]", + "datetime64[D]", + "datetime64[h]", + "datetime64[m]", + "datetime64[s]", + "datetime64[ms]", + "datetime64[us]", + "datetime64[ns]", + ], +) +def test_to_numpy_numpy_datetime(dtype): + """ + Test the _to_ndarray function with 1-D NumPy arrays of datetime. + + Time units "fs", "as", "ps" are not tested here because they can only represent a + small range of times in 1969-1970. + """ + array = np.array(["2024-01-01", "2024-01-02", "2024-01-03"], dtype=dtype) + result = _to_numpy(array) + _check_result(result, np.datetime64) + assert result.dtype == (dtype if isinstance(dtype, str) else "datetime64[D]") + npt.assert_array_equal(result, array) + + ######################################################################################## # Test the _to_numpy function with pandas.Series. # From 9e912baec890b1af009c0eac2b344229a9285e36 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 9 Jan 2025 10:38:40 +0800 Subject: [PATCH 19/35] clib.converison._to_numpy: Add tests for pandas.Series with datetime dtypes (#3670) --- pygmt/clib/conversion.py | 11 +++ pygmt/tests/test_clib_to_numpy.py | 107 ++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/pygmt/clib/conversion.py b/pygmt/clib/conversion.py index c54923705dc..52eec0d2479 100644 --- a/pygmt/clib/conversion.py +++ b/pygmt/clib/conversion.py @@ -192,6 +192,17 @@ def _to_numpy(data: Any) -> np.ndarray: numpy_dtype = np.float64 data = data.to_numpy(na_value=np.nan) + # Deal with timezone-aware datetime dtypes. + if isinstance(dtype, pd.DatetimeTZDtype): # pandas.DatetimeTZDtype + numpy_dtype = getattr(dtype, "base", None) + elif isinstance(dtype, pd.ArrowDtype) and hasattr(dtype.pyarrow_dtype, "tz"): + # pd.ArrowDtype[pa.Timestamp] + numpy_dtype = getattr(dtype, "numpy_dtype", None) + # TODO(pandas>=2.1): Remove the workaround for pandas<2.1. + if Version(pd.__version__) < Version("2.1"): + # In pandas 2.0, dtype.numpy_type is dtype("O"). + numpy_dtype = np.dtype(f"M8[{dtype.pyarrow_dtype.unit}]") # type: ignore[assignment, attr-defined] + array = np.ascontiguousarray(data, dtype=numpy_dtype) # Check if a np.object_ array can be converted to np.str_. diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index 79858bc04d4..31b6c2421e8 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -365,6 +365,113 @@ def test_to_numpy_pandas_date(dtype, expected_dtype): ) +pandas_old_version = pytest.mark.xfail( + condition=Version(pd.__version__) < Version("2.1"), + reason="pandas 2.0 bug reported in https://github.com/pandas-dev/pandas/issues/52705", +) + + +@pytest.mark.parametrize( + ("dtype", "expected_dtype"), + [ + # NumPy datetime64 types. Only unit 's'/'ms'/'us'/'ns' are supported. + pytest.param("datetime64[s]", "datetime64[s]", id="datetime64[s]"), + pytest.param("datetime64[ms]", "datetime64[ms]", id="datetime64[ms]"), + pytest.param("datetime64[us]", "datetime64[us]", id="datetime64[us]"), + pytest.param("datetime64[ns]", "datetime64[ns]", id="datetime64[ns]"), + # pandas.DatetimeTZDtype can be given in two ways [tz is required]: + # 1. pandas.DatetimeTZDtype(unit, tz) + # 2. String aliases: "datetime64[unit, tz]" + pytest.param( + "datetime64[s, UTC]", + "datetime64[s]", + id="datetime64[s, tz=UTC]", + marks=pandas_old_version, + ), + pytest.param( + "datetime64[s, America/New_York]", + "datetime64[s]", + id="datetime64[s, tz=America/New_York]", + marks=pandas_old_version, + ), + pytest.param( + "datetime64[s, +07:30]", + "datetime64[s]", + id="datetime64[s, +07:30]", + marks=pandas_old_version, + ), + # PyArrow timestamp types can be given in two ways [tz is optional]: + # 1. pd.ArrowDtype(pyarrow.Timestamp(unit, tz=tz)) + # 2. String aliases: "timestamp[unit, tz][pyarrow]" + pytest.param( + "timestamp[s][pyarrow]", + "datetime64[s]", + id="timestamp[s][pyarrow]", + marks=skip_if_no(package="pyarrow"), + ), + pytest.param( + "timestamp[ms][pyarrow]", + "datetime64[ms]", + id="timestamp[ms][pyarrow]", + marks=[skip_if_no(package="pyarrow"), pandas_old_version], + ), + pytest.param( + "timestamp[us][pyarrow]", + "datetime64[us]", + id="timestamp[us][pyarrow]", + marks=[skip_if_no(package="pyarrow"), pandas_old_version], + ), + pytest.param( + "timestamp[ns][pyarrow]", + "datetime64[ns]", + id="timestamp[ns][pyarrow]", + marks=skip_if_no(package="pyarrow"), + ), + pytest.param( + "timestamp[s, UTC][pyarrow]", + "datetime64[s]", + id="timestamp[s, UTC][pyarrow]", + marks=skip_if_no(package="pyarrow"), + ), + pytest.param( + "timestamp[s, America/New_York][pyarrow]", + "datetime64[s]", + id="timestamp[s, America/New_York][pyarrow]", + marks=skip_if_no(package="pyarrow"), + ), + pytest.param( + "timestamp[s, +08:00][pyarrow]", + "datetime64[s]", + id="timestamp[s, +08:00][pyarrow]", + marks=skip_if_no(package="pyarrow"), + ), + ], +) +def test_to_numpy_pandas_datetime(dtype, expected_dtype): + """ + Test the _to_numpy function with pandas.Series of datetime types. + """ + series = pd.Series( + [pd.Timestamp("2024-01-02T03:04:05"), pd.Timestamp("2024-01-02T03:04:06")], + dtype=dtype, + ) + result = _to_numpy(series) + _check_result(result, np.datetime64) + assert result.dtype == expected_dtype + + # Convert to UTC if the dtype is timezone-aware + if "," in str(dtype): # A hacky way to decide if the dtype is timezone-aware. + # TODO(pandas>=2.1): Simplify the if-else statement. + if Version(pd.__version__) < Version("2.1") and dtype.startswith("timestamp"): + # pandas 2.0 doesn't have the dt.tz_convert method for pyarrow.Timestamp. + series = pd.to_datetime(series, utc=True) + else: + series = series.dt.tz_convert("UTC") + # Remove time zone information and preserve local time. + expected_series = series.dt.tz_localize(tz=None) + npt.assert_array_equal(result, np.array(expected_series, dtype=expected_dtype)) + + ######################################################################################## # Test the _to_numpy function with PyArrow arrays. # From 504138458b9fde3d26f45abdce7b617c49c13e41 Mon Sep 17 00:00:00 2001 From: Wei Ji <23487320+weiji14@users.noreply.github.com> Date: Thu, 9 Jan 2025 17:37:33 +1300 Subject: [PATCH 20/35] CI: Separate jobs for publishing to TestPyPI and PyPI (#3742) --- .github/workflows/publish-to-pypi.yml | 58 ++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 2dbc12cbef1..15c66e1842b 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -35,13 +35,9 @@ on: # - main jobs: - publish-pypi: - name: Publish to PyPI + build: + name: Build distribution 📦 runs-on: ubuntu-latest - permissions: - # This permission is mandatory for OIDC publishing - id-token: write - if: github.repository == 'GenericMappingTools/pygmt' steps: - name: Checkout @@ -49,6 +45,7 @@ jobs: with: # fetch all history so that setuptools-scm works fetch-depth: 0 + persist-credentials: false - name: Set up Python uses: actions/setup-python@v5.3.0 @@ -74,11 +71,54 @@ jobs: echo "Generated files:" ls -lh dist/ - - name: Publish to Test PyPI + - name: Store the distribution packages + uses: actions/upload-artifact@v4.5.0 + with: + name: python-package-distributions + path: dist/ + + publish-to-testpypi: + name: Publish Python 🐍 distribution 📦 to TestPyPI + if: github.repository == 'GenericMappingTools/pygmt' + needs: + - build + runs-on: ubuntu-latest + environment: + name: testpypi + url: https://test.pypi.org/project/pygmt + permissions: + id-token: write # IMPORTANT: mandatory for trusted OIDC publishing + + steps: + - name: Download all the dists + uses: actions/download-artifact@v4.1.8 + with: + name: python-package-distributions + path: dist/ + + - name: Publish distribution 📦 to TestPyPI uses: pypa/gh-action-pypi-publish@v1.12.3 with: repository-url: https://test.pypi.org/legacy/ - - name: Publish to PyPI - if: startsWith(github.ref, 'refs/tags') + publish-pypi: + name: Publish Python 🐍 distribution 📦 to PyPI + if: github.repository == 'GenericMappingTools/pygmt' && startsWith(github.ref, 'refs/tags/') + needs: + - build + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/project/pygmt/ + permissions: + id-token: write # IMPORTANT: mandatory for trusted OIDC publishing + + steps: + - name: Download all the dists + uses: actions/download-artifact@v4.1.8 + with: + name: python-package-distributions + path: dist/ + + - name: Publish distribution 📦 to PyPI uses: pypa/gh-action-pypi-publish@v1.12.3 From 753a06ed6b3485af95dfff833bebfc3fc55493f2 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Fri, 10 Jan 2025 17:46:59 +0800 Subject: [PATCH 21/35] Bump to ruff 0.9.0, apply ruff 2025 style, and ignore A005 (stdlib-module-shadowing) violations (#3763) * Ignore A005 errors * Apply ruff styling changes in 2025 * Bump to ruff 0.9.0 --- doc/conf.py | 6 ++---- environment.yml | 2 +- .../tutorials/advanced/cartesian_histograms.py | 4 ++-- pygmt/_show_versions.py | 2 +- pygmt/encodings.py | 1 + pygmt/helpers/tempfile.py | 1 + pygmt/helpers/utils.py | 5 ++--- pygmt/io.py | 1 + pygmt/src/grd2xyz.py | 3 +-- pygmt/src/select.py | 1 + .../test_clib_virtualfile_from_stringio.py | 17 +++-------------- pygmt/tests/test_grdview.py | 2 +- pygmt/tests/test_info.py | 10 ++-------- 13 files changed, 19 insertions(+), 36 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 2b41acd3f61..f3cb59228c2 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -210,12 +210,10 @@ repository = "GenericMappingTools/pygmt" repository_url = "https://github.com/GenericMappingTools/pygmt" if __commit__: - commit_link = ( - f'{ __commit__[:8] }' - ) + commit_link = f'{__commit__[:8]}' else: commit_link = ( - f'{ __version__ }' + f'{__version__}' ) html_context = { "menu_links": [ diff --git a/environment.yml b/environment.yml index ce30de4825c..c51b2967fc2 100644 --- a/environment.yml +++ b/environment.yml @@ -27,7 +27,7 @@ dependencies: # Dev dependencies (style checks) - codespell - pre-commit - - ruff>=0.8.2 + - ruff>=0.9.0 # Dev dependencies (unit testing) - matplotlib-base - pytest>=6.0 diff --git a/examples/tutorials/advanced/cartesian_histograms.py b/examples/tutorials/advanced/cartesian_histograms.py index 7191d18cbfd..3a547fd92f5 100644 --- a/examples/tutorials/advanced/cartesian_histograms.py +++ b/examples/tutorials/advanced/cartesian_histograms.py @@ -348,7 +348,7 @@ # of the bin width # Offset ("+o") the bars to align each bar with the left limit of the corresponding # bin - barwidth=f"{binwidth/2}+o-{binwidth/4}", + barwidth=f"{binwidth / 2}+o-{binwidth / 4}", label="data01", ) @@ -359,7 +359,7 @@ fill="orange", pen="1p,darkgray,solid", histtype=0, - barwidth=f"{binwidth/2}+o{binwidth/4}", + barwidth=f"{binwidth / 2}+o{binwidth / 4}", label="data02", ) diff --git a/pygmt/_show_versions.py b/pygmt/_show_versions.py index d15bf0799c6..f0d4b4e3c2f 100644 --- a/pygmt/_show_versions.py +++ b/pygmt/_show_versions.py @@ -16,7 +16,7 @@ from pygmt.clib import Session, __gmt_version__ # Get semantic version through setuptools-scm -__version__ = f'v{version("pygmt")}' # e.g. v0.1.2.dev3+g0ab3cd78 +__version__ = f"v{version('pygmt')}" # e.g. v0.1.2.dev3+g0ab3cd78 __commit__ = __version__.split("+g")[-1] if "+g" in __version__ else "" # 0ab3cd78 diff --git a/pygmt/encodings.py b/pygmt/encodings.py index 0c7b7ddc895..09c749c4c82 100644 --- a/pygmt/encodings.py +++ b/pygmt/encodings.py @@ -1,3 +1,4 @@ +# noqa: A005 """ Character encodings supported by GMT. diff --git a/pygmt/helpers/tempfile.py b/pygmt/helpers/tempfile.py index 6995be1db98..70cc688156a 100644 --- a/pygmt/helpers/tempfile.py +++ b/pygmt/helpers/tempfile.py @@ -1,3 +1,4 @@ +# noqa: A005 """ Utilities for dealing with temporary file management. """ diff --git a/pygmt/helpers/utils.py b/pygmt/helpers/utils.py index b53942818c5..e32f5bbe03f 100644 --- a/pygmt/helpers/utils.py +++ b/pygmt/helpers/utils.py @@ -574,9 +574,8 @@ def launch_external_viewer(fname: str, waiting: float = 0) -> None: } match sys.platform: - case name if ( - (name == "linux" or name.startswith("freebsd")) - and (xdgopen := shutil.which("xdg-open")) + case name if (name == "linux" or name.startswith("freebsd")) and ( + xdgopen := shutil.which("xdg-open") ): # Linux/FreeBSD subprocess.run([xdgopen, fname], check=False, **run_args) # type:ignore[call-overload] case "darwin": # macOS diff --git a/pygmt/io.py b/pygmt/io.py index 9451de36c8f..a4ba289c7d9 100644 --- a/pygmt/io.py +++ b/pygmt/io.py @@ -1,3 +1,4 @@ +# noqa: A005 """ PyGMT input/output (I/O) utilities. """ diff --git a/pygmt/src/grd2xyz.py b/pygmt/src/grd2xyz.py index a44dc996c6d..b31d0013a25 100644 --- a/pygmt/src/grd2xyz.py +++ b/pygmt/src/grd2xyz.py @@ -145,8 +145,7 @@ def grd2xyz( if kwargs.get("o") is not None and output_type == "pandas": msg = ( - "If 'outcols' is specified, 'output_type' must be either 'numpy' " - "or 'file'." + "If 'outcols' is specified, 'output_type' must be either 'numpy' or 'file'." ) raise GMTInvalidInput(msg) # Set the default column names for the pandas DataFrame header. diff --git a/pygmt/src/select.py b/pygmt/src/select.py index ecd6d12bfad..a7db421a210 100644 --- a/pygmt/src/select.py +++ b/pygmt/src/select.py @@ -1,3 +1,4 @@ +# noqa: A005 """ select - Select data table subsets based on multiple spatial criteria. """ diff --git a/pygmt/tests/test_clib_virtualfile_from_stringio.py b/pygmt/tests/test_clib_virtualfile_from_stringio.py index ce6de238a88..62daaa688c8 100644 --- a/pygmt/tests/test_clib_virtualfile_from_stringio.py +++ b/pygmt/tests/test_clib_virtualfile_from_stringio.py @@ -43,14 +43,9 @@ def test_virtualfile_from_stringio(): Test the virtualfile_from_stringio method. """ data = io.StringIO( - "# Comment\n" - "H 24p Legend\n" - "N 2\n" - "S 0.1i c 0.15i p300/12 0.25p 0.3i My circle\n" - ) - expected = ( - ">\n" "H 24p Legend\n" "N 2\n" "S 0.1i c 0.15i p300/12 0.25p 0.3i My circle\n" + "# Comment\nH 24p Legend\nN 2\nS 0.1i c 0.15i p300/12 0.25p 0.3i My circle\n" ) + expected = ">\nH 24p Legend\nN 2\nS 0.1i c 0.15i p300/12 0.25p 0.3i My circle\n" assert _stringio_to_dataset(data) == expected @@ -66,13 +61,7 @@ def test_one_segment(): "6 7 8 9 FGHIJK LMN OPQ\n" "RSTUVWXYZ\n" ) - expected = ( - "> Segment 1\n" - "1 2 3 ABC\n" - "4 5 DE\n" - "6 7 8 9 FGHIJK LMN OPQ\n" - "RSTUVWXYZ\n" - ) + expected = "> Segment 1\n1 2 3 ABC\n4 5 DE\n6 7 8 9 FGHIJK LMN OPQ\nRSTUVWXYZ\n" assert _stringio_to_dataset(data) == expected diff --git a/pygmt/tests/test_grdview.py b/pygmt/tests/test_grdview.py index f73b1150e54..3be4ed7aa42 100644 --- a/pygmt/tests/test_grdview.py +++ b/pygmt/tests/test_grdview.py @@ -161,7 +161,7 @@ def test_grdview_with_perspective_and_zaxis_frame(xrgrid, region): a Transverse Mercator (T) projection. """ fig = Figure() - projection = f"T{(region[0]+region[1])/2}/{abs((region[2]+region[3])/2)}" + projection = f"T{(region[0] + region[1]) / 2}/{abs((region[2] + region[3]) / 2)}" fig.grdview( grid=xrgrid, projection=projection, diff --git a/pygmt/tests/test_info.py b/pygmt/tests/test_info.py index 3ac9f27c4e1..d055abb61ec 100644 --- a/pygmt/tests/test_info.py +++ b/pygmt/tests/test_info.py @@ -23,10 +23,7 @@ def test_info(): """ output = info(data=POINTS_DATA) expected_output = ( - f"{POINTS_DATA}: N = 20 " - "<11.5309/61.7074> " - "<-2.9289/7.8648> " - "<0.1412/0.9338>\n" + f"{POINTS_DATA}: N = 20 <11.5309/61.7074> <-2.9289/7.8648> <0.1412/0.9338>\n" ) assert output == expected_output @@ -57,10 +54,7 @@ def test_info_path(table): """ output = info(data=table) expected_output = ( - f"{POINTS_DATA}: N = 20 " - "<11.5309/61.7074> " - "<-2.9289/7.8648> " - "<0.1412/0.9338>\n" + f"{POINTS_DATA}: N = 20 <11.5309/61.7074> <-2.9289/7.8648> <0.1412/0.9338>\n" ) assert output == expected_output From 2bd524eb1e3780e598b3e8789a11be36042ee335 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Sat, 11 Jan 2025 13:58:20 +0800 Subject: [PATCH 22/35] Simplify doc/Makefile with sphinx-build make-mode (#3761) --- doc/Makefile | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/doc/Makefile b/doc/Makefile index b82215c44c4..5656fe80ab6 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,14 +1,12 @@ # Makefile for Sphinx documentation # You can set these variables from the command line. -SPHINXOPTS = -j auto -SPHINXBUILD = sphinx-build +SPHINXOPTS ?= -j auto +SPHINXBUILD ?= sphinx-build SPHINXAUTOGEN = sphinx-autogen +SOURCEDIR = . BUILDDIR = _build -# Internal variables. -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(SPHINXOPTS) . - .PHONY: help all api html server clean help: @@ -28,20 +26,20 @@ api: @echo $(SPHINXAUTOGEN) -i -t _templates -o api/generated api/*.rst -html: api +html latex: api @echo - @echo "Building HTML files." + @echo "Building "$@" files." @echo # Set PYGMT_USE_EXTERNAL_DISPLAY to "false" to disable external display - PYGMT_USE_EXTERNAL_DISPLAY="false" $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + PYGMT_USE_EXTERNAL_DISPLAY="false" $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + @echo "Build finished. The files are in $(BUILDDIR)/$@." html-noplot: api @echo @echo "Building HTML files without example plots." @echo - $(SPHINXBUILD) -D plot_gallery=0 -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + $(SPHINXBUILD) -D plot_gallery=0 -M html $(SPHINXOPTS) "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." From 77a2d5929dcbad02c8b10e40f3920f5f3131344d Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Sun, 12 Jan 2025 14:05:24 +0800 Subject: [PATCH 23/35] Fix the 'html-noplot' make target for docs (#3766) --- doc/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Makefile b/doc/Makefile index 5656fe80ab6..04b1c1ab549 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -39,7 +39,7 @@ html-noplot: api @echo @echo "Building HTML files without example plots." @echo - $(SPHINXBUILD) -D plot_gallery=0 -M html $(SPHINXOPTS) "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) + $(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) -D plot_gallery=0 @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." From 8b9ec5a6cea7b1352b52783534fe76bc9f99dd77 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 13 Jan 2025 08:11:09 +0800 Subject: [PATCH 24/35] Fix the bug of converting Python sequence of datetime-like objects (#3760) Convert unrecognized objects to datetime before converting to string dtype --- pygmt/clib/conversion.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pygmt/clib/conversion.py b/pygmt/clib/conversion.py index 52eec0d2479..7823aa32103 100644 --- a/pygmt/clib/conversion.py +++ b/pygmt/clib/conversion.py @@ -205,6 +205,11 @@ def _to_numpy(data: Any) -> np.ndarray: array = np.ascontiguousarray(data, dtype=numpy_dtype) + # Check if a np.object_ or np.str_ array can be converted to np.datetime64. + if array.dtype.type in {np.object_, np.str_}: + with contextlib.suppress(TypeError, ValueError): + return np.ascontiguousarray(array, dtype=np.datetime64) + # Check if a np.object_ array can be converted to np.str_. if array.dtype == np.object_: with contextlib.suppress(TypeError, ValueError): From 900e8d4f3cda1f29eb0a5c6d7cabe7cc6ae1f655 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 13 Jan 2025 08:22:32 +0800 Subject: [PATCH 25/35] clib.conversion._to_numpy: Add tests for Python sequence of datetime-like objects (#3758) Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com> --- .../tests/baseline/test_plot_datetime.png.dvc | 4 +- pygmt/tests/test_clib_to_numpy.py | 77 +++++++++++++++++-- pygmt/tests/test_plot.py | 7 +- 3 files changed, 80 insertions(+), 8 deletions(-) diff --git a/pygmt/tests/baseline/test_plot_datetime.png.dvc b/pygmt/tests/baseline/test_plot_datetime.png.dvc index 714104995ba..1450b29ef82 100644 --- a/pygmt/tests/baseline/test_plot_datetime.png.dvc +++ b/pygmt/tests/baseline/test_plot_datetime.png.dvc @@ -1,5 +1,5 @@ outs: -- md5: 583947facaa873122f0bf18137809cd4 - size: 12695 +- md5: 0a2eae0da1e3d5b71d7392de1c081346 + size: 13124 path: test_plot_datetime.png hash: md5 diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index 31b6c2421e8..40b45e466d8 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -2,8 +2,8 @@ Tests for the _to_numpy function in the clib.conversion module. """ +import datetime import sys -from datetime import date, datetime import numpy as np import numpy.testing as npt @@ -80,6 +80,70 @@ def test_to_numpy_python_types(data, expected_dtype): npt.assert_array_equal(result, data) +@pytest.mark.parametrize( + "data", + [ + pytest.param( + ["2018", "2018-02", "2018-03-01", "2018-04-01T01:02:03"], id="iso8601" + ), + pytest.param( + [ + datetime.date(2018, 1, 1), + datetime.datetime(2018, 2, 1), + datetime.date(2018, 3, 1), + datetime.datetime(2018, 4, 1, 1, 2, 3), + ], + id="datetime", + ), + pytest.param( + [ + np.datetime64("2018"), + np.datetime64("2018-02"), + np.datetime64("2018-03-01"), + np.datetime64("2018-04-01T01:02:03"), + ], + id="np_datetime64", + ), + pytest.param( + [ + pd.Timestamp("2018-01-01"), + pd.Timestamp("2018-02-01"), + pd.Timestamp("2018-03-01"), + pd.Timestamp("2018-04-01T01:02:03"), + ], + id="pd_timestamp", + ), + pytest.param( + [ + "2018-01-01", + np.datetime64("2018-02-01"), + datetime.datetime(2018, 3, 1), + pd.Timestamp("2018-04-01T01:02:03"), + ], + id="mixed", + ), + ], +) +def test_to_numpy_python_datetime(data): + """ + Test the _to_numpy function with Python sequence of datetime types. + """ + result = _to_numpy(data) + assert result.dtype.type == np.datetime64 + npt.assert_array_equal( + result, + np.array( + [ + "2018-01-01T00:00:00", + "2018-02-01T00:00:00", + "2018-03-01T00:00:00", + "2018-04-01T01:02:03", + ], + dtype="datetime64[s]", + ), + ) + + ######################################################################################## # Test the _to_numpy function with NumPy arrays. # @@ -603,9 +667,9 @@ def test_to_numpy_pyarrow_date(dtype, expected_dtype): Here we explicitly check the dtype and date unit of the result. """ data = [ - date(2024, 1, 1), - datetime(2024, 1, 2), - datetime(2024, 1, 3), + datetime.date(2024, 1, 1), + datetime.datetime(2024, 1, 2), + datetime.datetime(2024, 1, 3), ] array = pa.array(data, type=dtype) result = _to_numpy(array) @@ -649,7 +713,10 @@ def test_to_numpy_pyarrow_timestamp(dtype, expected_dtype): Reference: https://arrow.apache.org/docs/python/generated/pyarrow.timestamp.html """ - data = [datetime(2024, 1, 2, 3, 4, 5), datetime(2024, 1, 2, 3, 4, 6)] + data = [ + datetime.datetime(2024, 1, 2, 3, 4, 5), + datetime.datetime(2024, 1, 2, 3, 4, 6), + ] array = pa.array(data, type=dtype) result = _to_numpy(array) _check_result(result, np.datetime64) diff --git a/pygmt/tests/test_plot.py b/pygmt/tests/test_plot.py index 721b7841307..c2f2b846724 100644 --- a/pygmt/tests/test_plot.py +++ b/pygmt/tests/test_plot.py @@ -467,9 +467,14 @@ def test_plot_datetime(): fig.plot(x=x, y=y, style="a0.2c", pen="1p") # the Python built-in datetime and date - x = [datetime.date(2018, 1, 1), datetime.datetime(2019, 1, 1)] + x = [datetime.date(2018, 1, 1), datetime.datetime(2019, 1, 1, 0, 0, 0)] y = [8.5, 9.5] fig.plot(x=x, y=y, style="i0.2c", pen="1p") + + # Python sequence of pd.Timestamp + x = [pd.Timestamp("2018-01-01"), pd.Timestamp("2019-01-01")] + y = [5.5, 6.5] + fig.plot(x=x, y=y, style="d0.2c", pen="1p") return fig From d5e775016f70c0f0949947dd457178542abe0a35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 09:46:49 +1300 Subject: [PATCH 26/35] Build(deps): Bump mamba-org/setup-micromamba from 2.0.3 to 2.0.4 (#3769) Bumps [mamba-org/setup-micromamba](https://github.com/mamba-org/setup-micromamba) from 2.0.3 to 2.0.4. - [Release notes](https://github.com/mamba-org/setup-micromamba/releases) - [Commits](https://github.com/mamba-org/setup-micromamba/compare/v2.0.3...v2.0.4) --- updated-dependencies: - dependency-name: mamba-org/setup-micromamba dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/benchmarks.yml | 2 +- .github/workflows/cache_data.yaml | 2 +- .github/workflows/ci_docs.yml | 2 +- .github/workflows/ci_doctests.yaml | 2 +- .github/workflows/ci_tests.yaml | 2 +- .github/workflows/ci_tests_dev.yaml | 2 +- .github/workflows/ci_tests_legacy.yaml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 2fcc724ddae..f219ae76bdc 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -45,7 +45,7 @@ jobs: # Install Micromamba with conda-forge dependencies - name: Setup Micromamba - uses: mamba-org/setup-micromamba@v2.0.3 + uses: mamba-org/setup-micromamba@v2.0.4 with: environment-name: pygmt condarc: | diff --git a/.github/workflows/cache_data.yaml b/.github/workflows/cache_data.yaml index a005ba5e87b..0ce6ac025de 100644 --- a/.github/workflows/cache_data.yaml +++ b/.github/workflows/cache_data.yaml @@ -43,7 +43,7 @@ jobs: # Install Micromamba with conda-forge dependencies - name: Setup Micromamba - uses: mamba-org/setup-micromamba@v2.0.3 + uses: mamba-org/setup-micromamba@v2.0.4 with: environment-name: pygmt condarc: | diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index 06c7debb426..b02d953c0f7 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -80,7 +80,7 @@ jobs: # Install Micromamba with conda-forge dependencies - name: Setup Micromamba - uses: mamba-org/setup-micromamba@v2.0.3 + uses: mamba-org/setup-micromamba@v2.0.4 with: environment-name: pygmt condarc: | diff --git a/.github/workflows/ci_doctests.yaml b/.github/workflows/ci_doctests.yaml index 1d0cdac4f98..af28e6ac710 100644 --- a/.github/workflows/ci_doctests.yaml +++ b/.github/workflows/ci_doctests.yaml @@ -42,7 +42,7 @@ jobs: # Install Micromamba with conda-forge dependencies - name: Setup Micromamba - uses: mamba-org/setup-micromamba@v2.0.3 + uses: mamba-org/setup-micromamba@v2.0.4 with: environment-name: pygmt condarc: | diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index e155d7bd95c..2fad4df7a51 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -113,7 +113,7 @@ jobs: # Install Micromamba with conda-forge dependencies - name: Setup Micromamba - uses: mamba-org/setup-micromamba@v2.0.3 + uses: mamba-org/setup-micromamba@v2.0.4 with: environment-name: pygmt condarc: | diff --git a/.github/workflows/ci_tests_dev.yaml b/.github/workflows/ci_tests_dev.yaml index 6a026a5099f..c222de5d5a6 100644 --- a/.github/workflows/ci_tests_dev.yaml +++ b/.github/workflows/ci_tests_dev.yaml @@ -57,7 +57,7 @@ jobs: # Install Micromamba with conda-forge dependencies - name: Setup Micromamba - uses: mamba-org/setup-micromamba@v2.0.3 + uses: mamba-org/setup-micromamba@v2.0.4 with: environment-name: pygmt condarc: | diff --git a/.github/workflows/ci_tests_legacy.yaml b/.github/workflows/ci_tests_legacy.yaml index 1af5e00cd38..0c4ae574235 100644 --- a/.github/workflows/ci_tests_legacy.yaml +++ b/.github/workflows/ci_tests_legacy.yaml @@ -51,7 +51,7 @@ jobs: # Install Micromamba with conda-forge dependencies - name: Setup Micromamba - uses: mamba-org/setup-micromamba@v2.0.3 + uses: mamba-org/setup-micromamba@v2.0.4 with: environment-name: pygmt condarc: | From 540bba1db9d34191c880ccdd1289d7fbcb1264c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 09:48:29 +1300 Subject: [PATCH 27/35] Build(deps): Bump actions/upload-artifact from 4.5.0 to 4.6.0 (#3770) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.5.0 to 4.6.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.5.0...v4.6.0) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/cache_data.yaml | 2 +- .github/workflows/ci_tests.yaml | 2 +- .github/workflows/ci_tests_dev.yaml | 2 +- .github/workflows/publish-to-pypi.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cache_data.yaml b/.github/workflows/cache_data.yaml index 0ce6ac025de..46c37211424 100644 --- a/.github/workflows/cache_data.yaml +++ b/.github/workflows/cache_data.yaml @@ -76,7 +76,7 @@ jobs: # Upload the downloaded files as artifacts to GitHub - name: Upload artifacts to GitHub - uses: actions/upload-artifact@v4.5.0 + uses: actions/upload-artifact@v4.6.0 with: name: gmt-cache include-hidden-files: true diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index 2fad4df7a51..44416d210e0 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -177,7 +177,7 @@ jobs: # Upload diff images on test failure - name: Upload diff images if any test fails - uses: actions/upload-artifact@v4.5.0 + uses: actions/upload-artifact@v4.6.0 if: failure() with: name: artifact-${{ runner.os }}-${{ matrix.python-version }} diff --git a/.github/workflows/ci_tests_dev.yaml b/.github/workflows/ci_tests_dev.yaml index c222de5d5a6..6932a0d520b 100644 --- a/.github/workflows/ci_tests_dev.yaml +++ b/.github/workflows/ci_tests_dev.yaml @@ -187,7 +187,7 @@ jobs: # Upload diff images on test failure - name: Upload diff images if any test fails - uses: actions/upload-artifact@v4.5.0 + uses: actions/upload-artifact@v4.6.0 if: ${{ failure() }} with: name: artifact-GMT-${{ matrix.gmt_git_ref }}-${{ runner.os }} diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 15c66e1842b..917353ee85a 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -72,7 +72,7 @@ jobs: ls -lh dist/ - name: Store the distribution packages - uses: actions/upload-artifact@v4.5.0 + uses: actions/upload-artifact@v4.6.0 with: name: python-package-distributions path: dist/ From 199ef2b9db726eaa4122c7cc68b475742dad75b6 Mon Sep 17 00:00:00 2001 From: Michael Grund <23025878+michaelgrund@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:14:15 +0100 Subject: [PATCH 28/35] Add a gallery example showing the usage of Figure.hlines and Figure.vlines (#3755) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: actions-bot <58130806+actions-bot@users.noreply.github.com> Co-authored-by: Dongdong Tian Co-authored-by: Yvonne Fröhlich <94163266+yvonnefroehlich@users.noreply.github.com> --- examples/gallery/lines/hlines_vlines.py | 117 ++++++++++++++++++++++++ pygmt/src/hlines.py | 7 +- pygmt/src/vlines.py | 8 +- 3 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 examples/gallery/lines/hlines_vlines.py diff --git a/examples/gallery/lines/hlines_vlines.py b/examples/gallery/lines/hlines_vlines.py new file mode 100644 index 00000000000..ec0d73dddab --- /dev/null +++ b/examples/gallery/lines/hlines_vlines.py @@ -0,0 +1,117 @@ +""" +Horizontal and vertical lines +============================= + +The :meth:`pygmt.Figure.hlines` and :meth:`pygmt.Figure.vlines` methods allow to plot +horizontal and vertical lines in Cartesian, geographic and polar coordinate systems. +""" + +# %% +# Cartesian coordinate system +# --------------------------- +# In Cartesian coordinate systems lines are plotted as straight lines. + +import pygmt + +fig = pygmt.Figure() + +fig.basemap( + region=[0, 10, 0, 10], projection="X10c/10c", frame=["+tCartesian hlines", "af"] +) + +# Add a horizontal line at y=9 +fig.hlines(y=9, pen="1.5p,red3", label="Line 1") +# Add a horizontal line at y=8 with x from 2 to 8 +fig.hlines(y=8, xmin=2, xmax=8, pen="1.5p,gray30,-", label="Line 2") +# Add two horizontal lines at y=6 and y=7 both with x from 3 to 7 +fig.hlines(y=[6, 7], xmin=3, xmax=7, pen="1.5p,salmon", label="Lines 3 & 4") +# Add two horizontal lines at y=4 and y=5 both with x from 4 to 9 +fig.hlines(y=[4, 5], xmin=4, xmax=9, pen="1.5p,black,.", label="Lines 5 & 6") +# Add two horizontal lines at y=2 and y=3 with different x limits +fig.hlines( + y=[2, 3], xmin=[0, 1], xmax=[7, 7.5], pen="1.5p,dodgerblue3", label="Lines 7 & 8" +) +fig.legend(position="JBR+jBR+o0.2c", box="+gwhite+p1p") + +fig.shift_origin(xshift="w+2c") + +fig.basemap( + region=[0, 10, 0, 10], projection="X10c/10c", frame=["+tCartesian vlines", "af"] +) +# Add a vertical line at x=1 +fig.vlines(x=1, pen="1.5p,red3", label="Line 1") +# Add a vertical line at x=2 with y from 2 to 8 +fig.vlines(x=2, ymin=2, ymax=8, pen="1.5p,gray30,-", label="Line 2") +# Add two vertical lines at x=3 and x=4 both with y from 3 to 7 +fig.vlines(x=[3, 4], ymin=3, ymax=7, pen="1.5p,salmon", label="Lines 3 & 4") +# Add two vertical lines at x=5 and x=6 both with y from 4 to 9 +fig.vlines(x=[5, 6], ymin=4, ymax=9, pen="1.5p,black,.", label="Lines 5 & 6") +# Add two vertical lines at x=7 and x=8 with different y limits +fig.vlines( + x=[7, 8], ymin=[0, 1], ymax=[7, 7.5], pen="1.5p,dodgerblue3", label="Lines 7 & 8" +) +fig.legend() + +fig.show() + +# %% +# Geographic coordinate system +# ---------------------------- +# The same can be done in geographic coordinate systems where "horizontal" means lines +# are plotted along parallels (constant latitude) while "vertical" means lines are +# plotted along meridians (constant longitude). + +fig = pygmt.Figure() + +fig.basemap(region="g", projection="R15c", frame=["+tGeographic hlines", "af"]) +# Add a line at 70°N +fig.hlines(y=70, pen="1.5p,red3", label="Line 1") +# Add a line at 50°N with longitude limits at 20°E and 160°E +fig.hlines(y=50, xmin=20, xmax=160, pen="1.5p,dodgerblue3", label="Line 2") +# Add a line at 30°S with longitude limits at 60°E and 270°E +fig.hlines(y=-30, xmin=60, xmax=270, pen="1.5p,gray30,-", label="Line 3") +fig.legend() + +fig.shift_origin(xshift="w+2c") + +fig.basemap(region="g", projection="R15c", frame=["+tGeographic vlines", "af"]) +# Add a line at 70°E +fig.vlines(x=70, pen="1.5p,red3", label="Line 1") +# Add a line at 20°E with latitude limits at 50°S and 70°N +fig.vlines(x=120, ymin=-50, ymax=70, pen="1.5p,dodgerblue3", label="Line 2") +# Add a line at 230°E with latitude limits at 70°S and 80°N +fig.vlines(x=230, ymin=-70, ymax=80, pen="1.5p,gray30,-", label="Line 3") +fig.legend() + +fig.show() + +# %% +# Polar coordinate system +# ----------------------- +# When using polar coordinate systems "horizontal" means lines are plotted as arcs along +# a constant radius while "vertical" means lines are plotted as straight lines along +# radius at a specified azimuth. + +fig = pygmt.Figure() + +fig.basemap(region=[0, 360, 0, 1], projection="P10c", frame=["+tPolar hlines", "af"]) +# Add a line along radius=0.8 +fig.hlines(y=0.8, pen="1.5p,red3", label="Line 1") +# Add a line along radius=0.5 with azimuth limits at 30° and 160° +fig.hlines(y=0.5, xmin=30, xmax=160, pen="1.5p,dodgerblue3", label="Line 2") +# Add a line along radius=0.25 with azimuth limits at 60° and 270° +fig.hlines(y=0.25, xmin=60, xmax=270, pen="1.5p,gray30,-", label="Line 3") +fig.legend() + +fig.shift_origin(xshift="w+2c") + +fig.basemap(region=[0, 360, 0, 1], projection="P10c", frame=["+tPolar vlines", "af"]) +# Add a line along azimuth=120° +fig.vlines(x=120, pen="1.5p,red3", label="Line 1") +# Add a line along azimuth=190° with radius limits at 0.2 and 0.8 +fig.vlines(x=190, ymin=0.2, ymax=0.8, pen="1.5p,dodgerblue3", label="Line 2") +# Add a line along azimuth=320 with radius limits at 0.5 and 0.9 +fig.vlines(x=320, ymin=0.5, ymax=0.9, pen="1.5p,gray30,-", label="Line 3") +fig.legend() + +fig.show() diff --git a/pygmt/src/hlines.py b/pygmt/src/hlines.py index b277358d981..e2c390b4eea 100644 --- a/pygmt/src/hlines.py +++ b/pygmt/src/hlines.py @@ -38,9 +38,10 @@ def hlines( The term "horizontal" lines can be interpreted differently in different coordinate systems: - - **Cartesian** coordinate system: lines are plotted as straight lines. - - **Polar** projection: lines are plotted as arcs along a constant radius. - - **Geographic** projection: lines are plotted as parallels along constant latitude. + - **Cartesian**: lines are plotted as straight lines. + - **Polar**: lines are plotted as arcs along a constant radius. + - **Geographic**: lines are plotted as arcs along parallels (i.e., constant + latitude). Parameters ---------- diff --git a/pygmt/src/vlines.py b/pygmt/src/vlines.py index 2483df99f27..7f1919baa9c 100644 --- a/pygmt/src/vlines.py +++ b/pygmt/src/vlines.py @@ -38,10 +38,10 @@ def vlines( The term "vertical" lines can be interpreted differently in different coordinate systems: - - **Cartesian** coordinate system: lines are plotted as straight lines. - - **Polar** projection: lines are plotted as straight lines along radius. - - **Geographic** projection: lines are plotted as meridians along constant - longitude. + - **Cartesian**: lines are plotted as straight lines. + - **Polar**: lines are plotted as straight lines along a constant azimuth. + - **Geographic**: lines are plotted as arcs along meridians (i.e., constant + longitude). Parameters ---------- From 4ae940935aec75355ba2b336983f3e8260ba9963 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jan 2025 08:19:30 +0800 Subject: [PATCH 29/35] Build(deps): Bump astral-sh/setup-uv from 5.1.0 to 5.2.1 (#3779) Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 5.1.0 to 5.2.1. - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](https://github.com/astral-sh/setup-uv/compare/v5.1.0...v5.2.1) --- updated-dependencies: - dependency-name: astral-sh/setup-uv dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci_tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index 44416d210e0..28f4d120de3 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -154,7 +154,7 @@ jobs: GH_TOKEN: ${{ github.token }} - name: Install uv - uses: astral-sh/setup-uv@v5.1.0 + uses: astral-sh/setup-uv@v5.2.1 with: python-version: ${{ matrix.python-version }} From 8a09f75cae034ab0e01e1a29e46df65a116d9508 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jan 2025 08:22:26 +0800 Subject: [PATCH 30/35] Build(deps): Bump release-drafter/release-drafter from 6.0.0 to 6.1.0 (#3780) Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 6.0.0 to 6.1.0. - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/v6.0.0...v6.1.0) --- updated-dependencies: - dependency-name: release-drafter/release-drafter dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 118778439e1..5865c1af305 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -18,7 +18,7 @@ jobs: steps: # Drafts your next Release notes as Pull Requests are merged into "main" - - uses: release-drafter/release-drafter@v6.0.0 + - uses: release-drafter/release-drafter@v6.1.0 with: # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml config-name: release-drafter.yml From 4ca3677173657547dbedf40e282afc3991ac1c40 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 22 Jan 2025 16:55:44 +0800 Subject: [PATCH 31/35] CI: Bump to Ubuntu 22.04 in the GMT Legacy Tests workflow (#3777) --- .github/workflows/ci_tests_legacy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_tests_legacy.yaml b/.github/workflows/ci_tests_legacy.yaml index 0c4ae574235..dd371d715b5 100644 --- a/.github/workflows/ci_tests_legacy.yaml +++ b/.github/workflows/ci_tests_legacy.yaml @@ -34,7 +34,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-20.04, macos-13, windows-2019] + os: [ubuntu-22.04, macos-13, windows-2019] gmt_version: ['6.4'] timeout-minutes: 30 defaults: From a8365eecce0662725bfcc559aacdea738eda5a48 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Sun, 26 Jan 2025 22:10:04 +0800 Subject: [PATCH 32/35] Fix the dataset link in the RGB image gallery example (#3781) --- examples/gallery/images/rgb_image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gallery/images/rgb_image.py b/examples/gallery/images/rgb_image.py index 8afb2c93e25..af548c7c0b3 100644 --- a/examples/gallery/images/rgb_image.py +++ b/examples/gallery/images/rgb_image.py @@ -22,7 +22,7 @@ # %% # Read 3-band data from GeoTIFF into an xarray.DataArray object: with rioxarray.open_rasterio( - filename="https://oin-hotosm.s3.us-east-1.amazonaws.com/64d6a49a19cb3a000147a65b/0/64d6a49a19cb3a000147a65c.tif", + filename="https://oin-hotosm-temp.s3.us-east-1.amazonaws.com/64d6a49a19cb3a000147a65b/0/64d6a49a19cb3a000147a65c.tif", overview_level=5, ) as img: # Subset to area of Lāhainā in EPSG:32604 coordinates From 132dd223ad30de04c105545131c4647a5b7906e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jan 2025 09:52:46 +0800 Subject: [PATCH 33/35] Build(deps): Bump codecov/codecov-action from 5.1.2 to 5.3.1 (#3784) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.1.2 to 5.3.1. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v5.1.2...v5.3.1) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci_tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index 28f4d120de3..44b6276ac58 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -185,7 +185,7 @@ jobs: # Upload coverage to Codecov - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5.1.2 + uses: codecov/codecov-action@v5.3.1 if: success() || failure() with: use_oidc: true From 78fdb0454fbf94bc7887dc70c595d325926543a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jan 2025 13:19:27 +0800 Subject: [PATCH 34/35] Build(deps): Bump pypa/gh-action-pypi-publish from 1.12.3 to 1.12.4 (#3785) Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.12.3 to 1.12.4. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.12.3...v1.12.4) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/publish-to-pypi.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 917353ee85a..9432143a8f0 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -97,7 +97,7 @@ jobs: path: dist/ - name: Publish distribution 📦 to TestPyPI - uses: pypa/gh-action-pypi-publish@v1.12.3 + uses: pypa/gh-action-pypi-publish@v1.12.4 with: repository-url: https://test.pypi.org/legacy/ @@ -121,4 +121,4 @@ jobs: path: dist/ - name: Publish distribution 📦 to PyPI - uses: pypa/gh-action-pypi-publish@v1.12.3 + uses: pypa/gh-action-pypi-publish@v1.12.4 From 05c4ab9a9c560c86eba9797ae70730cbfc88538c Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Sun, 2 Feb 2025 09:44:38 +0800 Subject: [PATCH 35/35] Changelog entry for v0.14.1 (#3783) (#3786) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Yvonne Fröhlich <94163266+yvonnefroehlich@users.noreply.github.com> --- CITATION.cff | 6 +++--- README.md | 12 ++++++------ doc/_static/version_switch.js | 1 + doc/changes.md | 26 ++++++++++++++++++++++++++ doc/minversions.md | 1 + 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index a18999e8f2d..c9cd6c58c0a 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -76,9 +76,9 @@ authors: family-names: Wessel affiliation: University of Hawaiʻi at Mānoa, USA orcid: https://orcid.org/0000-0001-5708-7336 -date-released: 2024-12-31 -doi: 10.5281/zenodo.14535921 +date-released: 2025-02-01 +doi: 10.5281/zenodo.14742338 license: BSD-3-Clause repository-code: https://github.com/GenericMappingTools/pygmt type: software -version: 0.14.0 +version: 0.14.1 diff --git a/README.md b/README.md index ba4e47d8fdb..13f1f023e02 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ Feel free to cite our work in your research using the following BibTeX: ``` @software{ - pygmt_2024_14535921, + pygmt_2025_14742338, author = {Tian, Dongdong and Uieda, Leonardo and Leong, Wei Ji and @@ -157,12 +157,12 @@ Feel free to cite our work in your research using the following BibTeX: Quinn, Jamie and Wessel, Paul}, title = {{PyGMT: A Python interface for the Generic Mapping Tools}}, - month = dec, - year = 2024, + month = feb, + year = 2025, publisher = {Zenodo}, - version = {0.14.0}, - doi = {10.5281/zenodo.14535921}, - url = {https://doi.org/10.5281/zenodo.14535921} + version = {0.14.1}, + doi = {10.5281/zenodo.14742338}, + url = {https://doi.org/10.5281/zenodo.14742338} } ``` diff --git a/doc/_static/version_switch.js b/doc/_static/version_switch.js index 5450cb9d6af..c904d9c107b 100644 --- a/doc/_static/version_switch.js +++ b/doc/_static/version_switch.js @@ -12,6 +12,7 @@ var all_versions = { 'latest': 'latest', 'dev': 'dev', + 'v0.14.1': 'v0.14.1', 'v0.14.0': 'v0.14.0', 'v0.13.0': 'v0.13.0', 'v0.12.0': 'v0.12.0', diff --git a/doc/changes.md b/doc/changes.md index 8e7142d2fd8..322b09fffa4 100644 --- a/doc/changes.md +++ b/doc/changes.md @@ -1,5 +1,31 @@ # Changelog +## Release v0.14.1 (2025/02/01) + +[![Digital Object Identifier for PyGMT v0.14.1](https://zenodo.org/badge/DOI/10.5281/zenodo.14742338.svg)](https://doi.org/10.5281/zenodo.14742338) + +### Highlights + +- **Patch release fixing critical bugs in PyGMT v0.14.0** +- Fix the bug of converting Python sequence of datetime-like objects ([#3760](https://github.com/GenericMappingTools/pygmt/pull/3760)) + +### Maintenance + +- CI: Separate jobs for publishing to TestPyPI and PyPI ([#3742](https://github.com/GenericMappingTools/pygmt/pull/3742)) +- clib.conversion._to_numpy: Add tests for Python sequence of datetime-like objects ([#3758](https://github.com/GenericMappingTools/pygmt/pull/3758)) +- Fix an image in README.md (broken on PyPI) and rewrap to 88 characters ([#3740](https://github.com/GenericMappingTools/pygmt/pull/3740)) +- Fix the dataset link in the RGB image gallery example ([#3781](https://github.com/GenericMappingTools/pygmt/pull/3781)) +- Update License year to 2025 ([#3737](https://github.com/GenericMappingTools/pygmt/pull/3737)) + +**Full Changelog**: + +### Contributors + +* [Dongdong Tian](https://github.com/seisman) +* [Wei Ji Leong](https://github.com/weiji14) + +--- + ## Release v0.14.0 (2024/12/31) [![Digital Object Identifier for PyGMT v0.14.0](https://zenodo.org/badge/DOI/10.5281/zenodo.14535921.svg)](https://doi.org/10.5281/zenodo.14535921) diff --git a/doc/minversions.md b/doc/minversions.md index b10150b5ce1..5fb0d3d2236 100644 --- a/doc/minversions.md +++ b/doc/minversions.md @@ -41,6 +41,7 @@ compatibility reasons. | PyGMT Version | GMT | Python | NumPy | pandas | Xarray | |---|---|---|---|---|---| | [Dev][]* [] | {{ requires.gmt }} | {{ requires.python }} | {{ requires.numpy }} | {{ requires.pandas }} | {{ requires.xarray }} | +| [] | >=6.4.0 | >=3.11 | >=1.25 | >=2.0 | >=2023.04 | | [] | >=6.4.0 | >=3.11 | >=1.25 | >=2.0 | >=2023.04 | | [] | >=6.3.0 | >=3.10 | >=1.24 | >=1.5 | >=2022.09 | | [] | >=6.3.0 | >=3.10 | >=1.23 | >=1.5 | >=2022.06 |