pythonmatplotlib

Filling in plotted polygon/shape in ternary plot


I've plotted a polygon on a set of ternary axes using python-ternary and joined up the vertices to form a region on the plot. However, I'd like to be able to fill this region so that it is a shape rather than just the outline.

I used the following code to generate the axes and a set of vertices for the polygon:

import ternary
import numpy as np

# Define the scale (the sum of the three components for each point)
scale = 100

# Define the vertices of the polygon (each point should sum to the scale value)
polygon_vertices = [
    (20, 30, 50),
    (40, 30, 30),
    (30, 60, 10),
    (10, 50, 40)
]

# Create a figure with a ternary axis
figure, tax = ternary. Figure(scale=scale)
tax.set_title("Ternary Plot with Polygon Contour", fontsize=20)

# Plot the vertices of the polygon on the ternary plot
tax.scatter(polygon_vertices, marker='o', color='red', label='Vertices')

# Connect the vertices to form the contour of the polygon
for i in range(len(polygon_vertices)):
    next_i = (i + 1) % len(polygon_vertices)
    tax. Plot([polygon_vertices[i], polygon_vertices[next_i]], color='blue', linewidth=2, linestyle='--')

# Fill the polygon area using the ax. Fill method
tax.ax.fill(*zip(*polygon_vertices), color='lightblue', alpha=0.8, label='Polygon Area')

# Set axis labels
tax.left_axis_label("Component 1", fontsize=15)
tax.right_axis_label("Component 2", fontsize=15)
tax.bottom_axis_label("Component 3", fontsize=15)
tax.get_axes().axis('off')

# Set the gridlines
tax.gridlines(color="blue", multiple=10)

# Set ticks and gridlines
tax.ticks(axis='lbr', linewidth=1, multiple=10)
tax.gridlines(multiple=10, color="black")

# Display the legend
tax.legend()

tax.get_axes().set_aspect(1) #sets as equilateral triangle, needs to be the last step of plotting
tax._redraw_labels() #see above

# Display the plot
tax.show()

I get the resulting plot shown here:

Ternary Plot with Two Polygons plotted on wrong axes

As seen in the plot, it does plot the filled polygon but not in the correct place, and it plots two of them. I'm not sure why. How do I get the correct region filled?

Note: This is not the same question as Python fill polygon as this is Cartesian coordinates. When I try and plot with add_patches, I get a Type Error as Polygon can only take two positional arguments but I provide it with three, despite the earlier conversion of ternary to cartesian coordinates.


Solution

  • Calling tax.ax.fill() will go to matplotlib's standard ax.fill(). That function doesn't work with ternary coordinates, it needs regular x and y coordinates.

    As tax.fill() isn't implemented in the ternary library, you can use project_sequence() from ternary.helpers to convert the ternary coordinates to regular xs and ys. And then call matplotlib's tax.ax.fill() with those.

    import ternary
    from ternary.helpers import project_sequence
    import numpy as np
    
    # Define the scale (the sum of the three components for each point)
    scale = 100
    
    # Define the vertices of the polygon (each point should sum to the scale value)
    polygon_vertices = [
        (20, 30, 50),
        (40, 30, 30),
        (30, 60, 10),
        (10, 50, 40)
    ]
    
    # Create a figure with a ternary axis
    figure, tax = ternary.figure(scale=scale)
    tax.set_title("Ternary Plot with Polygon Contour", fontsize=20)
    
    # Plot the vertices of the polygon on the ternary plot
    tax.scatter(polygon_vertices, marker='o', color='red', label='Vertices')
    
    # Connect the vertices to form the contour of the polygon
    for i in range(len(polygon_vertices)):
        next_i = (i + 1) % len(polygon_vertices)
        tax.plot([polygon_vertices[i], polygon_vertices[next_i]], color='blue', linewidth=2, linestyle='--')
    
    # Fill the polygon area using the ax.fill method
    xs, ys = project_sequence(polygon_vertices)
    tax.ax.fill(xs, ys, color='lightblue', alpha=0.8, label='Polygon Area')
    
    # Set axis labels
    tax.left_axis_label("Component 1", fontsize=15)
    tax.right_axis_label("Component 2", fontsize=15)
    tax.bottom_axis_label("Component 3", fontsize=15)
    tax.ax.axis('off')
    
    # Set the gridlines
    tax.gridlines(color="blue", multiple=10)
    
    # Set ticks and gridlines
    tax.ticks(axis='lbr', linewidth=1, multiple=10)
    tax.gridlines(multiple=10, color="black")
    
    # Display the legend
    tax.legend()
    
    tax.ax.set_aspect(1) #sets as equilateral triangle, needs to be the last step of plotting
    tax._redraw_labels() #see above
    
    # Display the plot
    tax.show()
    

    python-ternary with filled polygon