1

I have been looking for an elegant/simple (working!) solution to create new complex markers for matplotlib.

For example, I would like to design a new marker which is the union of a set of vertices, for example (just an example), two petals which are symmetric (see verts1 and verts2) and two lines above and below (see verts3, and verts4). I would also like to have the petal possibly filled (or not) and the edgecolor of each vertices with possibly various colours (one petal is blue, the other is orange). How should I proceed?

A naive way forward is to do something like (for a double petal, the left one not being filled up, the right one being filled, see the definition for verts1, verts2, verts3, verts4 below):

Code

x = rand(10)
y = rand(10)
verts = [verts1, verts2, verts3, verts4]
fc = ['k', 'None', 'None', 'None']
ec = ['b', 'orange', 'k', 'k']

for lverts, lfc, lec in list(zip(verts, fc, ec)) :
    scatter(x, y, marker= (lverts, 0), facecolor=lfc, edgecolor=lec, s=1000, label='My symbol')

==> HOWEVER, since these are done in a for loop, it is not considered as a single marker when I do, for example, :

legend(loc=0)

QUESTION: how should I manage this? (couldn't find the answer on the net)

Suggestion are most welcome!

Thanks!

Definition for the vertices
if 1:
    # verts1:
    size, angrad = 10., 0.
    rx = 4. * size
    theta = np.linspace(-pi / 4., pi / 4., 151)
    x = rx*np.sqrt(cos(2.*theta))*cos(theta)
    y = rx*np.sqrt(cos(2.*theta))*sin(theta)
    rotx = x * cos(angrad) + y * sin(angrad)
    roty = -x * sin(angrad) + y * cos(angrad)
    verts1 = list(zip(rotx,roty))

    # verts2:
    size, angrad = 10.,  np.pi
    rx = 4. * size
    theta = np.linspace(-pi / 4., pi / 4., 151)
    x = rx*np.sqrt(cos(2.*theta))*cos(theta)
    y = rx*np.sqrt(cos(2.*theta))*sin(theta)
    rotx = x * cos(angrad) + y * sin(angrad)
    roty = -x * sin(angrad) + y * cos(angrad)
    verts2 = list(zip(rotx,roty))

    # verts3
    verts3 = list(zip([0.,0.],[0,0.1]))

    # verts4
    verts4 = list(zip([0.,0.],[-0.1,-0.03])) 
user1293231
  • 351
  • 1
  • 2
  • 3

1 Answers1

1

There are some questions around on how to create individual custom legend markers:

The idea is in those cases where all other method fail to create a custom handler class and use it to create a symbol inside the legend.

enter image description here

import matplotlib.pyplot as plt
import numpy as np


class Symbol(object):
    def __init__(self,fc, ec, markersize):
        size, angrad = 10., 0.
        rx = 4. * size
        theta = np.linspace(-np.pi / 4., np.pi / 4., 151)
        x = rx*np.sqrt(np.cos(2.*theta))*np.cos(theta)
        y = rx*np.sqrt(np.cos(2.*theta))*np.sin(theta)
        rotx = x * np.cos(angrad) + y * np.sin(angrad)
        roty = -x * np.sin(angrad) + y * np.cos(angrad)
        verts1 = list(zip(rotx,roty))
        # verts2:
        size, angrad = 10.,  np.pi
        rx = 4. * size
        theta = np.linspace(-np.pi / 4., np.pi / 4., 151)
        x = rx*np.sqrt(np.cos(2.*theta))*np.cos(theta)
        y = rx*np.sqrt(np.cos(2.*theta))*np.sin(theta)
        rotx = x * np.cos(angrad) + y * np.sin(angrad)
        roty = -x * np.sin(angrad) + y * np.cos(angrad)
        verts2 = list(zip(rotx,roty))
        # verts3
        verts3 = list(zip([0.,0.],[0,0.1]))
        # verts4
        verts4 = list(zip([0.,0.],[-0.1,-0.03])) 

        self.verts=[verts1,verts2,verts3,verts4]
        self.fc = fc
        self.ec = ec
        self.size=markersize
        self.group = list(zip(self.verts, self.fc, self.ec))

class SymbolHandler(object):
    def legend_artist(self, legend, orig_handle, fontsize, handlebox):

        x0, y0 = handlebox.xdescent, handlebox.ydescent
        width,height = handlebox.width, handlebox.height
        sc = []
        for lverts, lfc, lec in orig_handle.group:
            c = plt.scatter([width/2.-x0], [height/2.-y0], marker=(lverts, 0), 
                             facecolor=lfc, edgecolor=lec,s=orig_handle.size, 
                             transform=handlebox.get_transform())
            handlebox.add_artist(c)
            c.remove()
            sc.append(sc)
        return []


x = np.random.rand(4)
y = np.random.rand(4)
x2 = np.random.rand(4)
y2 = np.random.rand(4) 

fc = ['k', 'None', 'None', 'None']
ec = ['b', 'orange', 'k', 'k']
size =1000.
s = Symbol(fc,ec, size)
for lverts, lfc, lec in s.group:
    plt.scatter(x, y, marker= (lverts, 0), facecolor=lfc, edgecolor=lec, 
                s=size)


fc2 = ['crimson', 'limegreen', 'None', 'None']
ec2 = ['gold', 'gold', 'purple', 'k']
size2 =800.
s2 = Symbol(fc2,ec2, size2)
for lverts, lfc, lec in s2.group:
    plt.scatter(x2, y2, marker= (lverts, 0), facecolor=lfc, edgecolor=lec, 
                s=size2)


plt.legend([s,s2], ['label 1', "label 2"], handleheight=5, handlelength=3,
           handler_map={Symbol: SymbolHandler()})

plt.xlim(-0.3,1.3)
plt.ylim(-0.3,1.3)
plt.show()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712