pygmt icon indicating copy to clipboard operation
pygmt copied to clipboard

POC: Code PyGMT logo in PyGMT

Open yvonnefroehlich opened this issue 8 months ago • 6 comments

Description of proposed changes

Related to https://github.com/GenericMappingTools/pygmt/issues/1404#issuecomment-2718559910, create the PyGMT logo in Python with PyGMT.

  • Python script
  • Separat method Figure.pygmtlogo or Figure.logo_pygmt as part of the Figure class or addition to the method Figure.logo (which plots the GMT logo)
  • Gallery example

Preview:

  • Documentation: https://pygmt-dev--3849.org.readthedocs.build/en/3849/api/generated/pygmt.Figure.pygmtlogo.html
  • Gallery example: https://pygmt-dev--3849.org.readthedocs.build/en/3849/gallery/embellishments/pygmt_logo.html
  • Script by @seisman to generate all versions See https://github.com/GenericMappingTools/pygmt/pull/3849#issuecomment-2753372170; modified version to use the changed parameter names at https://github.com/GenericMappingTools/pygmt/pull/3849#issuecomment-2754121028
  • All combinations: https://pygmt-dev--3849.org.readthedocs.build/en/3849/_images/sphx_glr_pygmt_logo_004.png sphx_glr_pygmt_logo_004

Related to

  • #1404
  • #3848
  • #3845

TODO:

  • [x] Update intro comment of this PR
  • [x] Remove temporary files (eps)
  • [x] Include adding a wordmark and it's orientation to the input at the beginning
  • [x] Update codes to finale version of the visual
    • [x] space between horizontal line of letter G and letter M (https://github.com/GenericMappingTools/pygmt/pull/3849#discussion_r1994356616)
    • [x] ending of yellow line at the right side of letter M (https://github.com/GenericMappingTools/pygmt/pull/3849#discussion_r1994388088)
    • [x] clean boundary of shape (https://github.com/GenericMappingTools/pygmt/pull/3849/commits/0aea0b0dd0290af2d334b3cb3cfae7d664d7881b)
    • [ ] etc.
  • [x] Introduce variables
  • [x] Improve parameter names, e.g., shape="circle" | "hexagon" or hex=False | True
  • [x] Convert to function (remove show and rdeuce save to tempoary eps files, replot via Figure.image)
  • [x] Remove saving as eps file for rotation (first get ride of transparent margin)
  • [x] Remove saving as eps file for adding wordmark (different basemaps due to horizontal and vertical orientation)
  • [x] Convert to a method of the Figure class or addition to Figure.logo.
  • [ ] Add or expand gallery example to use the method
  • [ ] Explain the story behind the logo in the gallery example

Issues:

  • [x] Get ride of white or transparent margin
  • [x] Currently the rotation and adding the wordmark are done in new Figure objects using the saved eps files. Maybe this is not easy to handle when converting to a method of the Figure classe.
  • [ ] Decide about font for the wordmark (font Space Grotesk by Florian Karsten not available in GMT)
  • [ ] No direct export to SVG format possible
  • [ ] etc.

Reminders

  • [ ] Run make format and make check to make sure the code follows the style guide.
  • [ ] Add tests for new features or tests that would have caught the bug that you're fixing.
  • [ ] Add new public functions/methods/classes to doc/api/index.rst.
  • [ ] Write detailed docstrings for all functions/methods.
  • [ ] If wrapping a new module, open a 'Wrap new GMT module' issue and submit reasonably-sized PRs.
  • [ ] If adding new functionality, add an example to docstrings or tutorials.

Slash Commands

You can write slash commands (/command) in the first line of a comment to perform specific operations. Supported slash command is:

  • /format: automatically format and lint the code

yvonnefroehlich avatar Mar 13 '25 12:03 yvonnefroehlich

/format

yvonnefroehlich avatar Mar 13 '25 12:03 yvonnefroehlich

I feel the logo definition should be separated into a standalone method, similar to the method to plot the GMT logo. This would allow to plot the PyGMT logo by calling something like pygmt.Figure.pygmt_logo. The individual formats could be selected by different input parameters like you did or hex = True or darkmode = True.

michaelgrund avatar Mar 13 '25 12:03 michaelgrund

ruff v0.10.0 was released 2 hours ago causing the newly falling code style checks.

yvonnefroehlich avatar Mar 13 '25 21:03 yvonnefroehlich

  • separat method Figure.pygmtlogo or Figure.logo_pygmt as part of the Figure class or addition to the method Figure.logo (which plots the GMT logo)

@yvonnefroehlich You can follow the steps below:

  1. Create a pygmtlogo.py file under the pygmt/src/ directory
  2. Move the current pygmtlogo function to this file
  3. Add self as the first parameter of the pygmtlogo function
  4. Change fig.image (line 269) to self.image.
  5. Add from pygmt.src.pygmtlogo import pygmtlogo in pygmt/src/__init__.py
  6. Near line 429 in pygmt/figure.py, add pygmtlogo.

Then, you should be able to call Figure.pygmtlogo to add the logo.

seisman avatar Mar 16 '25 14:03 seisman

Click to view the script
from itertools import product

import pygmt

fig = pygmt.Figure()
# Logo without workmark.
fig.basemap(region=[0, 7, 0, 13], projection="x1c", frame="a1f1g1")
for x, y, darkmode in [(1, 3, False), (4, 3, True)]:
    for blackwhite, hexshape in product([False, True], repeat=2):
        fig.pygmtlogo(blackwhite=blackwhite, darkmode=darkmode, hexshape=hexshape, wordmark=False, position=f"g{x}/{y}+jTL+w2c")
        y += 3

fig.shift_origin(xshift=8)

# Logo with vertical wordmark.
fig.basemap(region=[0, 7, 0, 13], projection="x1c", frame="a1f1g1")
for x, y, darkmode in [(1, 3, False), (4, 3, True)]:
    for blackwhite, hexshape in product([False, True], repeat=2):
        fig.pygmtlogo(blackwhite=blackwhite, darkmode=darkmode, hexshape=hexshape, wordmark="vertical", position=f"g{x}/{y}+jTL+w2c")
        y += 3
 
fig.shift_origin(xshift=8)

# Logo with horizontal wordmark.
fig.basemap(region=[0, 20, 0, 13], projection="x1c", frame="a1f1g1")
for x, y, darkmode in [(1, 3, False), (11, 3, True)]:
    for blackwhite, hexshape in product([False, True], repeat=2):
        fig.pygmtlogo(blackwhite=blackwhite, darkmode=darkmode, hexshape=hexshape, wordmark="horizontal", position=f"g{x}/{y}+jTL+w0/2c")
        y += 3

fig.show(width=1000)
fig.savefig("PyGMT-logo.pdf")

I've written a script to plot all 24 variants of the PyGMT logo so that we can visualize the logo design easily. The base maps are linear projections (projection="x1c") with a scale of 1:1. The logos have a width (or height) of 2 cm.

PyGMT-logo

seisman avatar Mar 26 '25 06:03 seisman

I've written a script to plot all 24 variants of the PyGMT logo so that we can visualize the logo design easily. The base maps are linear projections (projection="x1c") with a scale of 1:1. The logos have a width (or height) of 2 cm.

Great. Thanks 🙂! Much nicer code then my looping over all parameters 🙈.


Updated version to use the changed parameter names:

Click to view the script
# %%
# All versions
# modified from
# https://github.com/GenericMappingTools/pygmt/pull/3849#issuecomment-2753372170
# by @seisman

import pygmt

fig = pygmt.Figure()

# Logo without workmark.
fig.basemap(region=[0, 7, 0, 13], projection="x1c", frame="a1f1g1")
for x, y, theme in [(1, 3, "light"), (4, 3, "dark")]:
    for color, shape in [
        (True, "circle"),
        (True, "hexagon"),
        (False, "circle"),
        (False, "hexagon"),
    ]:
        fig.pygmtlogo(
            color=color,
            theme=theme,
            shape=shape,
            wordmark=False,
            position=f"g{x}/{y}+jTL+w2c",
        )
        y += 3  # noqa: PLW2901

fig.shift_origin(xshift=8)

# Logo with vertical wordmark.
fig.basemap(region=[0, 7, 0, 13], projection="x1c", frame="a1f1g1")
for x, y, theme in [(1, 3, "light"), (4, 3, "dark")]:
    for color, shape in [
        (True, "circle"),
        (True, "hexagon"),
        (False, "circle"),
        (False, "hexagon"),
    ]:
        fig.pygmtlogo(
            color=color,
            theme=theme,
            shape=shape,
            wordmark="vertical",
            position=f"g{x}/{y}+jTL+w2c",
        )
        y += 3  # noqa: PLW2901

fig.shift_origin(xshift=8)

# Logo with horizontal wordmark.
fig.basemap(region=[0, 20, 0, 13], projection="x1c", frame="a1f1g1")
for x, y, theme in [(1, 3, "light"), (11, 3, "dark")]:
    for color, shape in [
        (True, "circle"),
        (True, "hexagon"),
        (False, "circle"),
        (False, "hexagon"),
    ]:
        fig.pygmtlogo(
            color=color,
            theme=theme,
            shape=shape,
            wordmark="horizontal",
            position=f"g{x}/{y}+jTL+w0/2c",
        )
        y += 3  # noqa: PLW2901

fig.show(width=1000)

yvonnefroehlich avatar Mar 26 '25 11:03 yvonnefroehlich

I've created an animation showing how the logo is plotted step by step (perspective angle is set to 0 in this case):

https://github.com/user-attachments/assets/dd1d2bf4-d48e-4013-9ef5-69ad86ba9996

I also draw some circles with radius of r0, r1, r2, r3, r4, respectively, which I find useful when designing and debugging the logo:

logo

I think we need to define one more radius for the outer radius of the curved horizontal line of letter T. The radius should be r2 + (r3-r4). In other words, we need to do the following:

  • rename r4 to r5
  • rename r3 to r4
  • rename r2 to r3
  • define the new r2=0.58125, and replace r3 + (r4-r5) at lines 193-194 with r2.

seisman avatar Nov 04 '25 12:11 seisman

I am thinking about the end of the compass line at the upper right edge of letter M:

version 1 version 2 (current version) version 3
pygmt_logo_letterM_compass_line_PART pygmt_logo_letterM_compass_line_cut01_PART pygmt_logo_letterM_compass_line_cut02_PART
pygmt_logo_letterM_compass_line_circles_PART pygmt_logo_letterM_compass_line_cut01_circles_PART pygmt_logo_letterM_compass_line_cut02_circles_PART

So far, we use version 2. I feel version 3 is too detailed. I have a preference for version 1. What do the others think?

yvonnefroehlich avatar Nov 05 '25 13:11 yvonnefroehlich

So far, we use version 2. I feel version 3 is too detailed. I have a preference for version 1. What do the others think?

I also like version 1, then we can remove lines 149-152.

seisman avatar Nov 05 '25 14:11 seisman

  1. Updated animation for producing the logo step by step. It looks pretty good: animation

  2. The "hexagon"-shape logo is not ideal, especially how the yellow compass lines intersect with the blue hexagon outline. I'm thinking if we should drop the hexagon logo in the initial version and add it later if there is still interest.

logo
  1. What about renaming variables like color_blue to blue, to make the codes shorter?

seisman avatar Nov 07 '25 02:11 seisman

I made two small updates (i) slightly smaller arrow head for letter T and (ii) shorten the vertical compass line:

old new
pygmt_logo_largearrow_PRAT pygmt_logo_smallarrow_PART
pygmt_logo_longline_PART pygmt_logo_shortline_PART

So far I removed color from the variable names color_blue, color_yellow, and color_red. I agree, the for the hexagon shape, the compass lines are not optimal.

yvonnefroehlich avatar Nov 07 '25 19:11 yvonnefroehlich

I adjusted the position of the wordmark:

circle hexagon (for completness included)
pygmt_logo_wm_circle_horizontal pygmt_logo_wm_hexagon_horizontal
pygmt_logo_wm_circle_vertical pygmt_logo_wm_hexagon_vertical

In case we keep or when we later add the hexagon version, we should make the circle and hexagon versions have the same overall size.

yvonnefroehlich avatar Nov 07 '25 21:11 yvonnefroehlich

Just tried to adjust the codes to make the hexagon version look at bit better.

Figure 2025-11-08 001348 (18) pygmt_logo_circle_wm_hor_NOlines pygmt_logo_circle_wm_vert_NOlines animation_circle_02
Figure 2025-11-08 001634 (18) pygmt_logo_hexagon_wm_hor_NOlines pygmt_logo_hexagon_wm_vert_NOlines animatin_hexagon_02

Now, the compass lines match the hexagon better, but letter T is a bit short.

yvonnefroehlich avatar Nov 07 '25 22:11 yvonnefroehlich

@yvonnefroehlich Just in case it’s helpful, I’ve pushed the layered-animation branch, which contains a single commit fd97aea. This commit adds a simple feature: it saves the current figure as frame-xxx.png each time a plotting method is called.

When you run the Figure.pygmtlogo method, you’ll get multiple frame-xxx.png files, which you can then convert into an animation using a command like:

magick -delay 50 -loop 0 frame-*.png animation.gif

seisman avatar Nov 08 '25 04:11 seisman