marcharper / python-ternary

:small_red_triangle: Ternary plotting library for python with matplotlib
MIT License
726 stars 156 forks source link

area filling #106

Open altanir84 opened 5 years ago

altanir84 commented 5 years ago

Great code.

Would be very helpfull to implement the tax.fill() feature, to create base plots with filled areas, like the Dickinson, 1970 Qt ternary plot. Thank you!

vmeschke commented 3 years ago

I manually implemented this by using some of the code for 'background' fill!

marcharper commented 3 years ago

Hi @vmeschke, do you want to share / contribute an example?

vmeschke commented 3 years ago

@marcharper I would love to! What's the best way to do so?

marcharper commented 3 years ago

@vmeschke If you have an image and some code to generate it, you can add it to the examples directory. Otherwise you can paste the info here and I'll add it.

vmeschke commented 3 years ago

Here's an example of code and the filling demo! I just modified the tax.fill method to take an unspecified number of points. I can talk more/add additional comments if helpful. filled_ternary

Code: import ternary import pandas as pd import math import itertools import numpy as np from pymatgen.core import Element, Composition from matplotlib import cm import matplotlib.pyplot as plt

def permute_point(p, permutation=None): """ Permutes the point according to the permutation keyword argument. The default permutation is "012" which does not change the order of the coordinate. To rotate counterclockwise, use "120" and to rotate clockwise use "201".""" if not permutation: return p return [p[int(permutation[i])] for i in range(len(p))]

def unzip(l): """[(a1, b1), ..., (an, bn)] ----> ([a1, ..., an], [b1, ..., bn])""" return list(zip(*l))

def project_point(p, permutation=None): """ Maps (x,y,z) coordinates to planar simplex. Parameters

p: 3-tuple
    The point to be projected p = (x, y, z)
permutation: string, None, equivalent to "012"
    The order of the coordinates, counterclockwise from the origin
"""
permuted = permute_point(p, permutation=permutation)
a = permuted[0]
b = permuted[1]
x = a + b/2.
y = (np.sqrt(3)/2) * b
return np.array([x, y])

def fill_region(ax, color, points, pattern=None, zorder=-1000, alpha=None): """Draws a triangle behind the plot to serve as the background color for a given region.""" vertices = map(project_point, points) xs, ys = unzip(vertices) poly = ax.fill(xs, ys, facecolor=color, edgecolor=color, hatch=pattern, zorder=zorder, alpha=alpha) return poly

def main(): scale = 100 fontsize = 12 figure, tax = ternary.figure(scale=scale) tax.gridlines(color="grey", multiple=10)

# corner points of the regions. Any number of points works.
regions = [[(0, 0, 100), (25, 0, 75), (25, 50, 25)],
            [(25, 0, 75), (50, 50, 0), (30, 40, 30), (25, 50, 25)],
            [(50, 50, 0), (43, 57, 0), (25, 50, 25), (70, 10, 20)],
            [(40, 50, 10), (28.57, 71.43, 0), (25, 50, 25), (10, 80, 10), (20, 30, 50)]]

# Generating colors for the regions
cmap = cm.plasma
colorlist = []
for i in np.arange(0,1,1/len(regions)):
    colorlist.append(cmap(i))

# generate the filled regions and make them all different colors
colorindex = 0
for r in regions:
    fill_region(tax.get_axes(), color=colorlist[colorindex], points=r, alpha=.7)
    colorindex += 1

# Final plot formatting
tax.boundary(linewidth=2)
tax.get_axes().axis('off')
tax.clear_matplotlib_ticks()
tax.get_axes().set_aspect(1)
tax._redraw_labels()
tax.savefig('filled_ternary.png')

if name == 'main': main()

vmeschke commented 3 years ago

If you add the following code to the 'ternary_axes_subplot.py' file, you should be able to call tax.fill_region() with the proper arguments and have it work ok. It works locally for me.

Additional Import: from .helpers import unzip

Method:

    def fill_region(self, color, points, pattern=None, zorder=-1000, alpha=None):
        """Draws a triangle behind the plot to serve as the background color
        for a given region."""
        ax = self.get_axes()
        vertices = map(project_point, points)
        xs, ys = unzip(vertices)
        poly = ax.fill(xs, ys, facecolor=color, edgecolor=color, hatch=pattern, zorder=zorder, alpha=alpha)
        return poly