Open YaraRossi opened 6 days ago
👋 Thanks for opening your first issue here! Please make sure you filled out the template with as much detail as possible. You might also want to take a look at our contributing guidelines and code of conduct.
I'm not sure if there's a way to get a circular colorbar, but GMT/PyGMT does have cyclic colormaps listed at https://docs.generic-mapping-tools.org/6.5/reference/cpts.html#id4, and if you use those, there will be a circle/cycle symbol that denotes it is cyclic:
import pygmt
fig = pygmt.Figure()
pygmt.makecpt(cmap="SCM/corkO", series=[0, 360, 45], continuous=True)
fig.colorbar(cmap=True, region=[0,2,4,8], projection="X10c")
fig.show()
produces
If you want an actual circular colorbar though, that would need to be done in upstream GMT repo https://github.com/GenericMappingTools/gmt, or another workaround.
Thanks for the input. However, I think to understand the data better and faster, and actual circle would be really helpful. I now made a workaround that creates a *.png file with the circular colorbar. That figure can afterwards be inserted into the pygmt figure through fig.image(). Attached is an example file and the figure with the real data. In case anyone else needs the same feature.
Example file: Circular_colormap_example.txt
Figure:
Yes, I agree that a circular colorbar would be more appropriate for your case. Also, thanks for sharing your code!
That said, I would advise you to use a proper cyclic colormap, because the current one has a discontinuity at 0 degree / North (the color jumps from green to blue abruptly). Since you are already using a Scientific Colour Map (tofino), you might want to consider one of the cyclic colormaps at https://www.fabiocrameri.ch/colourmaps/ where the transition from 359degrees to 1 degree is smoother. Specifically romaO
, bamO
, brocO
, corkO
, or vikO
In PyGMT, the circular colorbar can be done by plotting wedges via Figure.plot
with style="W"
.
Some time ago I created a circular colorbar in PyGMT: https://github.com/yvonnefroehlich/gmt-pygmt-plotting/pull/23/files
thank you for sharing your code yvonnefroehlich, I'll check it out!
As @seisman mentioned it's also easy to do that by plotting wedges within a 360° range @YaraRossi. Also adding a hole in the center without needing any transparency is straightforward.
Much easier compared to what I did during my PhD :sweat_smile::sweat_smile::sweat_smile: https://github.com/michaelgrund/GMT-plotting/blob/main/009_paper_GR2020/pygmt_jn_fig_s10/GR_2020_Fig_S10.ipynb.
I experimented with wedges the last few weeks and maybe it's worth to consider if we want to implement some high level functions like circular colorbars, pie charts, doughnut plots etc. based on using wedges with plot
. I hope I have time to expand my POCs during the holiday season.
Added the potential functions to the list in #2797.
import pygmt
import numpy as np
def colorbar_az(cmap = "romaO", center_x = 1, center_y = 1, radius_inner = 1.5, radius_outer = 2.5):
values = np.full(360, 1)
# calc fraction within full circle
fracs = [value / 360 * 100 for value in values]
# scale colormap
pygmt.makecpt(cmap=cmap, series=[0, len(values)-1, 1])
start_angle = 0
for i, frac in enumerate(fracs):
end_angle = start_angle + (frac / 100) * 360
fig.plot(x = center_x,
y = center_y,
style = f"w{radius_outer}/{start_angle}/{end_angle}+i{radius_inner}",
zvalue = i,
fill = "+z",
cmap = True
)
start_angle = end_angle
fig = pygmt.Figure()
fig.coast(
region=[-125, -122, 47, 49],
projection="M6c",
land="grey",
water="lightblue",
shorelines=True,
frame="a",
)
colorbar_az(center_x = -123, center_y = 48)
fig.show()
Just checking this out - nice approach to use wedges (with inner diameter) here! Thanks for sharing your ideas and codes @seisman and @michaelgrund! Would suggest to create a NumPy array with all wedges before plotting to make the code faster (similiar as I did for the rotated recangles, which I use in my code).
import numpy as np
import pygmt
def colorbar_az(cmap="romaO", center_x=0, center_y=0, diameter_inner=1.5, diameter_outer=2.5):
values = np.full(360, 1)
# calc fraction within full circle
fracs = [value / 360 * 100 for value in values]
# scale colormap
pygmt.makecpt(cmap=cmap, series=[0, len(values)-1, 1])
# Create a Numpy array with all wedges
start_angle = 0
data_wedges = np.zeros([360, 6])
for i, frac in enumerate(fracs):
end_angle = start_angle + (frac / 100) * 360
data_wedge_temp = np.array([
center_x, center_y, start_angle, diameter_outer, start_angle, end_angle,
])
data_wedges[i,:] = data_wedge_temp
start_angle = end_angle
# Plot all wedges with one call of Figure.plot()
fig.plot(data=data_wedges, style=f"w+i{diameter_inner}c", cmap=True)
This is another simplified version based on @yvonnefroehlich's script:
import numpy as np
import pygmt
def colorbar_az(cmap="romaO", x0=0, y0=0, diameter_inner=1.5, diameter_outer=2.5, step=1.0):
pygmt.makecpt(cmap=cmap, series=[0, 360])
angles = np.arange(0, 360, step)
# Create the data for the wedges
data_wedges = np.column_stack([
np.full_like(angles, x0), # x
np.full_like(angles, y0), # y
angles, # For color
angles, # start_angle
angles + step # end_angle
])
# Plot the all wedges with one call of Figure.plot()
fig.plot(data=data_wedges, style=f"w{diameter_outer}c+i{diameter_inner}c", cmap=True)
fig = pygmt.Figure()
fig.basemap(region=[-5, 5, -5, 5], frame=True, projection="X10c/10c")
colorbar_az()
fig.show()
I added another variable that allows to rotate the color, so that 0 can be defined where ever people need it. Additionally, it would be great to have lables for example E, N, W, S, but I couldn't manage to locate them next to the wedges automatically. With fig.text() they stayed in the cartesian coordinate system.
import numpy as np
import pygmt
def colorbar_az( fig="fig", cmap="romaO", x0=0, y0=0, diameter_inner=1.5, diameter_outer=2.5, step=1.0,start_azimuth = "0:East, 90:North, 180:West, 270:South"):
pygmt.makecpt(cmap=cmap, series=[0, 360])
angles = np.arange(0, 360, step)
# Create the data for the wedges
data_wedges = np.column_stack([
np.full_like(angles, x0), # x
np.full_like(angles, y0), # y
angles, # For color
angles + start_azimuth, # start_angle
angles + step + start_azimuth # end_angle
])
# Plot the all wedges with one call of Figure.plot()
fig.plot(data=data_wedges, style=f"w{diameter_outer}c+i{diameter_inner}c", cmap=True)
Description of the desired feature
For various datasets a linear colorbar is appropriate. However when azimuthal data is plotted [0 - 360°] is would make sense to have a circular colorbar that actually represents the direction the data is plotted. This could either be set on the side of the plot or as an inset. At the moment I don't think this exists yet and my only work around would be to save a *.png figure and then add it to the pygmt plot through fig.image() function.
An example is the direction of tilt. It's possible to visualize this with vectors, but when the directions get more heterogeneous it gets messy. This is an example of where the direction of the ground tilt is visualized according to azimuth. The linear colorbar is confusing, as the colors are actually continuous and azimuthal.
the colorbar (colorcircle?) should be something like this:
Are you willing to help implement and maintain this feature?
Yes