Skip to content

Conversation

@seisman
Copy link
Member

@seisman seisman commented Nov 16, 2025

See #4168 (comment) for context.

This PR implements the Position class.

Usage:

>>> import pygmt
>>> from pygmt.params import Position
>>> fig = pygmt.Figure()
>>> fig.basemap(region=[0, 10, 0, 10], projection="X10c", frame=True)
>>> fig.logo(
...:     position=Position((3, 3), cstype="mapcoords", anchor="ML", offset=(0.2, 0.2)),
...:     box=True,
...: )
>>> fig.show()

GMT Documentation: https://docs.generic-mapping-tools.org/dev/reference/features.html#reference-and-anchor-point-specification

Preview: https://pygmt-dev--4212.org.readthedocs.build/en/4212/api/generated/pygmt.params.Position.html


#: Specify the reference point on the plot. The method of defining the reference
#: point is controlled by ``type``, and the exact location is set by ``position``.
location: Sequence[float | str] | AnchorCode
Copy link
Member Author

Choose a reason for hiding this comment

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

position or location?

Copy link
Member

Choose a reason for hiding this comment

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

I think location is better as the parameter is already called position and we overall will have the Position class.

Copy link
Member

Choose a reason for hiding this comment

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

The docstring above calls this 'reference point', so how about something like ref or refpt?

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe refpoint?

In fact, I'd prefer to make it a positional-only parameter, so it will be used like Position("TL", type="inside"), Position((1, 2)).

In this case, the specific parameter name doesn't really matter, though dataclasses doesn't support positional-only attributes.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I did think of positional-only, but a little tricky with dataclasses as you said. Ok to got with refpoint, but let's see what @yvonnefroehlich thinks.

Copy link
Member

Choose a reason for hiding this comment

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

I am fine with refpoint. Unfortunately, I have only little experience with positional-only parameters.

@seisman seisman changed the title POC: Add the Position class for GMT embellishment placement Add the Position class for GMT embellishment placement Nov 25, 2025
@seisman seisman added the feature Brand new feature label Nov 25, 2025
@seisman seisman added this to the 0.18.0 milestone Nov 25, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces the Position class to provide a structured, Pythonic way to specify positions for GMT embellishments (logo, scale, rose, etc.). The class translates between PyGMT's long-form parameters and GMT's position syntax string format.

Key Changes:

  • Implements Position class supporting five coordinate systems (map, plot, box, inside, outside)
  • Provides validation for location, type, anchor, and offset parameters
  • Includes comprehensive test coverage for various position specifications

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.

File Description
pygmt/params/position.py New Position class implementation with validation and GMT string conversion
pygmt/tests/test_params_position.py Test suite covering position types, anchor/offset combinations, and validation
pygmt/params/__init__.py Exports the new Position class
doc/api/index.rst Adds Position to the API documentation index

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@seisman seisman added final review call This PR requires final review and approval from a second reviewer and removed needs review This PR has higher priority and needs review. labels Dec 7, 2025
@seisman
Copy link
Member Author

seisman commented Dec 7, 2025

Ping @GenericMappingTools/pygmt-maintainers for final reviews. I plan to merge it in 48 hours if no further comments.

Comment on lines +21 to +23
.. figure:: https://docs.generic-mapping-tools.org/dev/_images/GMT_anchor.png
:width: 600 px
:align: center
Copy link
Member

Choose a reason for hiding this comment

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

I feel we should make the images here and the one in the Technical References at https://pygmt-dev--4212.org.readthedocs.build/en/4212/techref/justification_codes.html consistent regarding the used colors.

Copy link
Member Author

Choose a reason for hiding this comment

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

Here we use the image from the GMT documentation, mainly because it's technically difficult to generate an image dynamically in docstrings.


**Reference Point**

The *reference point* can be specified in five different ways using the ``type`` and
Copy link
Member

Choose a reason for hiding this comment

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

I am unsure about type as attribute name, as type or type() seems to be a reserved keyword or function by Python itself:

position_class_attribute_type

We often use the name of a parameter or attribute for the name of the variable passed to the parameter or attribute:

import pygmt
from pygmt.params import Position

v = 5

# %% 
type(v)
# works and outputs int

fig = pygmt.Figure()
fig.basemap(region=[0, 10, 0, 10], projection="X10c", frame=True)
fig.logo(position=Position("TL", type="inside", offset="0.2c"), box=True)
fig.show()

# %%
type = "inside"
type(v)
# fails as function was overwritten

fig = pygmt.Figure()
fig.basemap(region=[0, 10, 0, 10], projection="X10c", frame=True)
fig.logo(position=Position("TL", type=type, offset="0.2c"), box=True)
fig.show()

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, type is not an ideal name. Several options are:

  • coord_type: accurate but a little too long
  • kind: a general term, similar to type
  • cs_type: cs is short for "coordinate system", not that bad.

Copy link
Member

Choose a reason for hiding this comment

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

I am fine with coord_type or cs_type. coord_type is more descriptive, but I have a slight preference for cs_type, as I feel for the arguments inside and outside "coordinate system" makes more sense. As geopandas uses crs for "coordinate reference systems", cs for "coordinate system" should be fine.

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe cstype, since cs is already a shorthand for coordinate system?

seisman and others added 5 commits December 9, 2025 10:38
Co-authored-by: Yvonne Fröhlich <94163266+yvonnefroehlich@users.noreply.github.com>
Co-authored-by: Yvonne Fröhlich <94163266+yvonnefroehlich@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Brand new feature final review call This PR requires final review and approval from a second reviewer

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

4 participants