#!/usr/bin/env python# -*- coding: utf-8 -*-## This file is part of the `pypath` python module## Copyright 2014-2023# EMBL, EMBL-EBI, Uniklinik RWTH Aachen, Heidelberg University## Authors: see the file `README.rst`# Contact: Dénes Türei (turei.denes@gmail.com)## Distributed under the GPLv3 License.# See accompanying file LICENSE.txt or copy at# https://www.gnu.org/licenses/gpl-3.0.html## Website: https://pypath.omnipathdb.org/#fromfuture.utilsimportiteritemsfrompast.builtinsimportxrange,range,reduceimportosimportsystry:importcairoexcept:sys.stdout.write('Module `cairo` is not available.''\nSome plotting functionalities won\'t be accessible.\n')importmathimporttimefrompypath.share.commonimport*importpypath.share.commonascommonimportpypath_common._constantsas_constimportpypath.share.sessionassession_mod__all__=['Plot','InterSet']try:importigraphexceptModuleNotFoundError:sys.stdout.write('Module `igraph` is not available.''\nSome plotting functionalities won\'t be accessible.\n')
[docs]def__init__(self,xsizes,intersects,outf='cairotest.pdf',width=1024,height=1024,bgcol='embl_gray125',cols=None,interscols=None,ysizes=None,ycols=None,skip=3.5,margin=24,mincircle=5,cellpadding=4):forkey,valiniteritems(locals()):setattr(self,key,val)self.colors={'embl_green':(115,179,96,255),'embl_blue':(0,102,102,255),'embl_yellow':(250,183,0,255),'embl_red':(227,62,62,255),'embl_black':(0,0,0,255),'embl_gray875':(32,32,32,255),'embl_gray75':(64,64,64,255),'embl_gray625':(96,96,96,255),'embl_gray50':(128,128,128,255),'embl_gray25':(192,192,192,255),'embl_gray125':(224,224,224,255),'white':(255,255,255,255)}# positions of circle labels:self.clabels=[(0.5,math.sqrt(3)/-2.0),(math.sqrt(3)/2.0,-0.5),(math.sqrt(2)/2.0,math.sqrt(2)/-2.0)]self.palette=[self.colors[x]forxin['embl_green','embl_blue','embl_yellow']]self.fontpal=[self.colors[x]forxin['embl_gray875','white','embl_gray875']]self.font='HelveticaNeueLT Std Lt'self.bgcol=self.bgcoliftype(self.bgcol)istupleelseself.colors[self.bgcol]# set parameters for x:# list of column labels (first elements of tuples in x list)self.xlabs=[x[0]forxinself.xsizes]# colors of sets in column headersself.xcols=self.get_colors(self.xsizes)# set sizes:self.xsizes=self.get_sizes(self.xsizes)# same for y:self.ylabs=[y[0]foryinself.ysizes]ifself.ysizesisnotNoneelseself.xlabsself.ycols=self.get_colors(self.ysizes) \
ifself.ysizesisnotNoneelseself.xcolsself.ysizes=self.get_sizes(self.ysizes) \
ifself.ysizesisnotNoneelseself.xsizes# margin:# margin is either a single integer, or a tuple of 4 integers:self.margin=self.marginiftype(self.margin)istupleelse(self.margin,)*4# table:# proportions of cell sizes:self.xcellsize=[3,1]+[3]*len(self.xsizes)self.ycellsize=[3,1]+[3]*len(self.ysizes)# sizes of table cells:self.xcoo=self.cells(self.xcellsize,self.margin[0],self.width-self.margin[1])self.ycoo=self.cells(self.ycellsize,self.margin[2],self.height-self.margin[3])# width and height of diagram cells:self.cellw=self.xcoo[0]self.cellh=self.ycoo[0]# largest circle fit in the cells:self.maxcircle=min(self.cellw,self.cellh)/2-2*self.cellpaddingself.maxarea=pow(self.maxcircle,2)*math.piself.minarea=pow(self.mincircle,2)*math.pi# scaling circle sizes between min and max circle sizeself.xcircsize=self.scale_sizes(self.xsizes)self.ycircsize=self.scale_sizes(self.ysizes)ssize=self.scale_sizes([x['size']forxinself.intersects.values()])fori,kinenumerate(self.intersects.keys()):self.intersects[k]['ssize']=ssize[i]if'color'notinself.intersects[k]:self.intersects[k]['color']=self.palette[0:len(ssize[i])]
[docs]defdraw(self):''' main function of this class '''self.srf=cairo.PDFSurface(self.outf,self.width,self.height)self.ctx=cairo.Context(self.srf)self.draw_table()self.colnames()self.rownames()self.draw_circles()self.srf.finish()self.srf.flush()
defmax_text(self,labels,width):pts=[]forlabinlabels:pts.append(self.fit_text(lab,width))returnmin(pts)deffit_text(self,txt,width,pt=24,padding=2):overf=1whileoverf>0:self.ctx.select_font_face(self.font,cairo.FONT_SLANT_NORMAL,cairo.FONT_WEIGHT_NORMAL)self.ctx.set_font_size(pt)overf=self.ctx.text_extents(txt)[2]-width+paddingpt*=0.95returnptdefget_colors(self,sizes,palette=None):palette=paletteifpaletteisnotNoneelseself.palettereturn[[xx[1]ifxx[1]isnotNoneelsepalette[i]fori,xxinenumerate(x[1:])]forxinsizes]defget_sizes(self,sizes):return[[xx[0]fori,xxinenumerate(x[1:])]forxinsizes]defscale_sizes(self,sizes):scaled=[math.sqrt(x/math.pi)forxinself.scale([iforiiinsizesforiinii],self.minarea,self.maxarea)]lens=[len(s)forsinsizes]return[scaled[sum(lens[:i]):sum(lens[:i])+l]fori,linenumerate(lens)]defrgb1(self,rgb256):returnrgb256ifnotany([i>1foriinrgb256]) \
elsetuple([x/float(255)forxinrgb256])defset_alpha(self,col,alpha):returntuple(list(col[0:3])+[alpha])defdraw_circles(self):self.clabpt=self.fit_text('00000',self.cellw/7,padding=0)y=self.margin[2]+self.cellh/float(2)forxiinrange(2,len(self.xcoo)):x=self.margin[0]+sum(self.xcoo[:xi])+self.cellw/float(2)fori,szinenumerate(self.xcircsize[xi-2]):self.circle(x,y,sz,self.rgb1(self.xcols[xi-2][i]))self.label(str(self.xsizes[xi-2][i]),*tuple([a+b*szfora,binzip([x,y],list(self.clabels[i]))]),pt=self.clabpt,c=self.colors['embl_gray875'],center=True,vcenter=True,rot=-45)x=self.margin[0]+self.cellw/float(2)foryiinrange(2,len(self.ycoo)):y=self.margin[2]+sum(self.ycoo[:yi])+self.cellh/float(2)fori,szinenumerate(self.ycircsize[yi-2]):self.circle(x,y,sz,self.rgb1(self.ycols[yi-2][i]))self.label(str(self.ysizes[yi-2][i]),*tuple([a+b*szfora,binzip([x,y],list(self.clabels[i]))]),pt=self.clabpt,c=self.colors['embl_gray875'],center=True,vcenter=True,rot=-45)# intersectionsforyiinrange(2,len(self.ycoo)):y=self.margin[2]+sum(self.ycoo[:yi])+self.cellh/float(2)forxiinrange(2,len(self.xcoo)):x=self.margin[0]+ \
sum(self.xcoo[:xi])+self.cellw/float(2)fori,szinenumerate(self.intersects[(self.xlabs[xi-2],self.ylabs[yi-2])]['ssize']):self.circle(x,y,sz,self.rgb1(self.intersects[(self.xlabs[xi-2],self.ylabs[yi-2])]['color'][i]))self.label(str(self.intersects[(self.xlabs[xi-2],self.ylabs[yi-2])]['size'][i]),*tuple([a+b*szfora,binzip([x,y],list(self.clabels[i]))]),pt=self.clabpt,c=self.colors['embl_gray875'],center=True,vcenter=True,rot=-45)''' self.label(str(self.intersects[(self.xlabs[xi-2], self.ylabs[yi-2])]['size']), x, y, self.intersects[(self.xlabs[xi-2], self.ylabs[yi-2])]['ssize'][i] * 0.5 + 3, self.colors['white']) '''defcircle(self,x,y,r,c):c=self.set_alpha(c,0.5)self.ctx.set_source_rgba(*c)self.ctx.arc(x,y,r,0,2*math.pi)self.ctx.fill()deflabel(self,txt,x,y,pt,c,center=True,rot=0.0,vcenter=False):c=self.rgb1(c)self.ctx.select_font_face(self.font,cairo.FONT_SLANT_NORMAL,cairo.FONT_WEIGHT_NORMAL)self.ctx.set_font_size(pt)self.ctx.save()self.ctx.translate(x,y)self.ctx.rotate(math.radians(rot))ifcenter:ext=self.ctx.text_extents(txt)self.ctx.translate(-ext[2]/2.0,0.0)ifvcenter:ext=self.ctx.text_extents(txt)self.ctx.translate(0.0,-ext[3]/2.0)self.ctx.move_to(0,0)self.ctx.set_source_rgba(*c)self.ctx.show_text(txt)self.ctx.restore()defcolnames(self):# font size for labels:self.xlabpt=self.max_text(self.xlabs,self.cellw)y=self.margin[2]+self.ycoo[0]+self.ycoo[1]/2.0forxiinrange(2,len(self.xcoo)):x=self.margin[0]+sum(self.xcoo[:xi])+self.cellw/2.0lab=self.xlabs[xi-2]self.label(lab,x,y,self.xlabpt,self.colors['embl_gray875'])defrownames(self):# font size for labels:self.xlabpt=self.max_text(self.xlabs,self.cellw)x=self.margin[0]+self.xcoo[0]+self.xcoo[1]/2.0foryiinrange(2,len(self.ycoo)):y=self.margin[2]+sum(self.ycoo[:yi])+self.cellh/2.0lab=self.ylabs[yi-2]self.label(lab,x,y,self.xlabpt,self.colors['embl_gray875'],rot=-90)defdraw_table(self):bg=self.rgb1(self.bgcol)self.ctx.set_source_rgba(*bg)forxiinrange(0,len(self.xcoo)):foryiinrange(0,len(self.ycoo)):ulx=self.margin[0]+sum(self.xcoo[:xi])uly=self.margin[2]+sum(self.ycoo[:yi])# print 'Drawing rectangle at (%f, %f), size (%f, %f)' % \# (ulx + self.skip, uly + self.skip, self.xcoo[xi] - self.skip,# self.ycoo[yi] - self.skip)self.ctx.rectangle(ulx+self.skip,uly+self.skip,self.xcoo[xi]-self.skip,self.ycoo[yi]-self.skip)self.ctx.stroke_preserve()self.ctx.fill()defcells(self,props,mi,ma):rng=ma-miuni=rng/float(sum(props))return[x*uniforxinprops]defscale(self,lst,lower,upper):return[((x-min(set(lst)-set([0])))/float(max(lst)-min(set(lst)-set([0])))*(upper-lower)+lower)ifx!=0else0forxinlst]