Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allows for a color function to be specified to plot_field_lines() #70

Merged
merged 13 commits into from
Aug 18, 2021
1 change: 1 addition & 0 deletions changelog/70.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allows user to specify color via a color function to :meth:`~sunkit_pyvista.plotter.SunpyPlotter.plot_field_lines`.
17 changes: 15 additions & 2 deletions examples/field_lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

sunkit-pyvista can be used to plot field lines from `pfsspy`.
"""
import matplotlib.pyplot as plt
import numpy as np
import pfsspy
from matplotlib import colors
from pfsspy import tracing
from pfsspy.sample_data import get_gong_map

Expand Down Expand Up @@ -55,7 +57,18 @@
frame=gong_map.coordinate_frame)
field_lines = tracer.trace(seeds, output_)

# We plot the field lines
plotter.plot_field_lines(field_lines)

# We can also specify a color function while plotting the field lines.
# This function takes a single field line, and returns a color either
# in the form of a string, (r,g,b) or (r,g,b,a) tuple.
# In this case we use a Matplotlib norm and colormap to return a tuple of RGBA values.
def my_fline_color_func(field_line):
norm = colors.LogNorm(vmin=1, vmax=1000)
cmap = plt.get_cmap('viridis')
return cmap(norm(np.abs(field_line.expansion_factor)))


# Plotting the field lines
plotter.plot_field_lines(field_lines, color_func=my_fline_color_func)

plotter.show()
28 changes: 25 additions & 3 deletions sunkit_pyvista/plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,24 +373,46 @@ def plot_quadrangle(self, bottom_left, top_right=None, width: u.deg = None,
self.plotter.add_mesh(quad_block, color=color, **kwargs)
self._add_mesh_to_dict(block_name='quadrangles', mesh=quad_block)

def plot_field_lines(self, field_lines, **kwargs):
def plot_field_lines(self, field_lines, color_func=None, **kwargs):
"""
Plot magnetic field lines from `pfsspy`.

Parameters
----------
field_lines : `pfsspy.fieldline.FieldLines`
Field lines to be plotted.
color_func : function
Function to get the color for each field line.
jeffreypaul15 marked this conversation as resolved.
Show resolved Hide resolved
If not given, defaults to showing closed field lines in black,
and open field lines in blue (positive polarity) or red (negative polarity).
jeffreypaul15 marked this conversation as resolved.
Show resolved Hide resolved
The function must have the signature::

def color_func(field_line: pfsspy.fieldline.FieldLine) -> color:

Where ``color`` is any color that `pyvista` recognises
(e.g. a RGBA tuple, a RGB tuple, a color string)
**kwargs :
Keyword arguments are handed to `pyvista.Plotter.add_mesh`.
"""
if not color_func:
def color_func(field_line):
color = {0: 'black', -1: 'tab:blue', 1: 'tab:red'}.get(field_line.polarity)
return colors.to_rgb(color)

field_line_meshes = pv.MultiBlock([])
for field_line in field_lines:
grid = self._coords_to_xyz(field_line.coords.ravel())
field_line_mesh = pv.StructuredGrid(grid[:, 0], grid[:, 1], grid[:, 2])
color = {0: 'black', -1: 'tab:blue', 1: 'tab:red'}.get(field_line.polarity)
color = color_func(field_line)
opacity = 1
if isinstance(color, tuple):
color = list(color)
if len(color) == 4:
opacity = color[3]
color = color[:3]

field_line_mesh.add_field_array([color], 'color')
self.plotter.add_mesh(field_line_mesh, color=color, **kwargs)
self.plotter.add_mesh(field_line_mesh, color=color, opacity=opacity, **kwargs)
field_line_meshes.append(field_line_mesh)

self._add_mesh_to_dict(block_name='field_lines', mesh=field_line_meshes)
Expand Down
Binary file modified sunkit_pyvista/tests/image_cache/field_lines_figure.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 8 additions & 1 deletion sunkit_pyvista/tests/test_plotting.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
"""
This file contains figure comparison tests.
"""
import matplotlib.pyplot as plt
import numpy as np
import pfsspy
import pytest
import pyvista
from matplotlib import colors
from pfsspy import tracing
from pfsspy.sample_data import get_gong_map

Expand Down Expand Up @@ -78,6 +80,11 @@ def test_field_lines_figure(aia171_test_map, plotter, verify_cache_image):
frame=gong_map.coordinate_frame)
field_lines = tracer.trace(seeds, output_)

def color_function(field_line):
norm = colors.LogNorm(vmin=1, vmax=1000)
cmap = plt.get_cmap('magma')
return cmap(norm(np.abs(field_line.expansion_factor)))

plotter.plot_map(aia171_test_map)
plotter.plot_field_lines(field_lines)
plotter.plot_field_lines(field_lines, color_function)
plotter.show(cpos=(0, 1, 0), before_close_callback=verify_cache_image)
13 changes: 12 additions & 1 deletion sunkit_pyvista/tests/test_pyvista.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@

import pathlib

import matplotlib.pyplot as plt
import numpy as np
import pfsspy
import pytest
import pyvista as pv
from matplotlib import colors
from pfsspy import tracing
from pfsspy.sample_data import get_gong_map

Expand Down Expand Up @@ -131,7 +134,7 @@ def test_multi_block(plotter):
assert plotter.all_meshes['solar_axis'][0].n_points == 101


def test_field_lines(aia171_test_map, plotter):
def test_field_lines_and_color_func(aia171_test_map, plotter):
Copy link
Member

Choose a reason for hiding this comment

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

Is this a figure test? If not can we add a figure test with the custom color function?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was going to suggest that, do we have a new figure test for this one? Or modify the existing figure to use colorfunc instead of the default colours

Copy link
Member

Choose a reason for hiding this comment

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

Lets modify the existing one

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yep, I've done that

gong_fname = get_gong_map()
gong_map = smap.Map(gong_fname)
nrho = 35
Expand All @@ -152,6 +155,14 @@ def test_field_lines(aia171_test_map, plotter):
assert isinstance(plotter.all_meshes['field_lines'][0],
pv.core.composite.MultiBlock)

def color_func(field_line):
norm = colors.LogNorm(vmin=1, vmax=1000)
cmap = plt.get_cmap('viridis')
return cmap(norm(np.abs(field_line.expansion_factor)))

plotter = SunpyPlotter()
plotter.plot_field_lines(field_lines, color_func=color_func)


def test_save_and_load(aia171_test_map, plotter, tmp_path):
plotter.plot_map(aia171_test_map)
Expand Down