00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 import logging, math, numpy
00019 from PyPedal import pyp_graphics
00020 from PyPedal import pyp_network
00021 from PyPedal import pyp_utils
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 def get_color_32(a,cmin,cmax):
00033 """
00034 Convert a float value to one of a continuous range of colors.
00035 Rewritten to use recipe 9.10 from the O'Reilly Python Cookbook.
00036 Returns 32-bit colors rather than 16-bit colors.
00037 """
00038 try:
00039 a = float( a - cmin ) / ( cmax - cmin )
00040 except ZeroDivisionError:
00041 a = 0.5
00042 blue = min((max((4*(0.75-a),0.)),1.))
00043 red = min((max((4*(a-0.25),0.)),1.))
00044 green = min((max((4*math.fabs(a-0.5)-1.,0)),1.))
00045 _r = '%2x' % int(255*red)
00046 if _r[0] == ' ':
00047 _r = '0%s' % _r[1]
00048 _g = '%2x' % int(255*green)
00049 if _g[0] == ' ':
00050 _g = '0%s' % _g[1]
00051 _b = '%2x' % int(255*blue)
00052 if _b[0] == ' ':
00053 _b = '0%s' % _b[1]
00054 _triple = '#%s%s%s' % (_r,_g,_b)
00055 return _triple
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066 def color_pedigree(pedobj,metric='descendants',places=2,drawer='new',**kw):
00067 """
00068 color_pedigree() forms a graph object from a pedigree object and determines the
00069 proportion of animals in a pedigree that are descendants of each animal in the
00070 pedigree. The results will be used to feed draw_colored_pedigree().
00071 """
00072 _dprop = {}
00073 if metric == 'descendants':
00074 _pedgraph = pyp_network.ped_to_graph(pedobj)
00075
00076
00077 for _p in pedobj.pedigree:
00078 _dcount = pyp_network.find_descendants(_pedgraph,_p.animalID,[])
00079 if len(_dcount) < 1:
00080 _dprop[_p.animalID] = 0.0
00081 else:
00082 _dprop[_p.animalID] = round(float(len(_dcount)) / float(pedobj.metadata.num_records), places)
00083 del(_pedgraph)
00084 elif metric == 'sons':
00085 for _p in pedobj.pedigree:
00086 _dprop[_p.animalID] = float(len(_p.sons))
00087
00088 else:
00089 return 0
00090 if drawer == 'new':
00091 new_draw_colored_pedigree(pedobj, _dprop, **kw)
00092 else:
00093 draw_colored_pedigree(pedobj, _dprop, **kw)
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119 def draw_colored_pedigree(pedobj, shading, gfilename='pedigree', gtitle='My_Pedigree', gformat='jpg', gsize='f', gdot='1', gorient='l', gdirec='', gname=0, gfontsize=10, garrow=1, gtitloc='b', gtitjust='c', ghatch='hatch',
00120 gprog='dot'):
00121 """
00122 draw_colored_pedigree() uses the pydot bindings to the graphviz library to produce a
00123 directed graph of your pedigree with paths of inheritance as edges and animals as
00124 nodes. If there is more than one generation in the pedigree as determind by the "gen"
00125 attributes of the animals in the pedigree, draw_pedigree() will use subgraphs to try
00126 and group animals in the same generation together in the drawing. Nodes will be
00127 colored based on the number of outgoing connections (number of offspring).
00128 """
00129 from pyp_utils import string_to_table_name
00130 _gtitle = string_to_table_name(gtitle)
00131
00132 if gtitloc not in ['t','b']:
00133 gtitloc = 'b'
00134 if gtitjust not in ['c','l','r']:
00135 gtitjust = 'c'
00136
00137 print '[DEBUG]: Entered draw_colored_pedigree()'
00138
00139
00140 import pydot
00141
00142
00143
00144 gens = pedobj.metadata.unique_gen_list
00145
00146 g = pydot.Dot(label=gtitle, labelloc=gtitloc, labeljust=gtitjust, graph_name=_gtitle, type='graph', strict=False, suppress_disconnected=True, simplify=True)
00147
00148
00149 try:
00150 gfontsize = int(gfontsize)
00151 except:
00152 gfontsize = 10
00153 if gfontsize < 10:
00154 gfontsize = 10
00155 gfontsize = str(gfontsize)
00156
00157 g.set_page("8.5,11")
00158 g.set_size("7.5,10")
00159 if gorient == 'l':
00160 g.set_orientation("landscape")
00161 else:
00162 g.set_orientation("portrait")
00163 if gsize != 'l':
00164 g.set_ratio("auto")
00165 if gdirec == 'RL':
00166 g.set_rankdir('RL')
00167 elif gdirec == 'LR':
00168 g.set_rankdir('LR')
00169 else:
00170 pass
00171 g.set_center('true')
00172 g.set_concentrate('true')
00173 g.set_ordering('out')
00174 if gformat not in g.formats:
00175 gformat = 'jpg'
00176
00177 colormin = min(shading.values())
00178 colormax = max(shading.values())
00179 color_map = {}
00180 if len(gens) <= 1:
00181 animalCounter = 0
00182 print '\t[DEBUG]: Only one generation'
00183 for _m in pedobj.pedigree:
00184 animalCounter = animalCounter + 1
00185 if numpy.fmod(animalCounter,pedobj.kw['counter']) == 0:
00186 print '\t[DEBUG]: Records read: %s ' % ( animalCounter )
00187
00188 if gname:
00189 _node_name = _m.name
00190 else:
00191 _node_name = _m.animalID
00192 _an_node = pydot.Node(_node_name)
00193 _an_node.set_fontname('Helvetica')
00194
00195 _an_node.set_fontsize(gfontsize)
00196 _an_node.set_height('0.35')
00197 if _m.sex == 'M' or _m.sex == 'm':
00198 _an_node.set_shape('box')
00199 elif _m.sex == 'F' or _m.sex == 'f':
00200 _an_node.set_shape('ellipse')
00201 else:
00202 pass
00203
00204 if _m.userField == ghatch:
00205 _an_node.set_style('filled,peripheries=2')
00206 else:
00207 _an_node.set_style('filled')
00208 try:
00209 _color = color_map[shading[_m.animalID]]
00210 except KeyError:
00211 _color = get_color_32(shading[_m.animalID], colormin, colormax)
00212 color_map[shading[_m.animalID]] = _color
00213 print '\t[DEBUG]: %s added to cache' % ( _color )
00214 _an_node.set_fillcolor(_color)
00215 g.add_node(_an_node)
00216
00217 if _m.sireID != pedobj.kw['missing_parent']:
00218 if gname:
00219 if garrow:
00220 g.add_edge(pydot.Edge(pedobj.pedigree[int(_m.sireID)-1].name, _m.name))
00221 else:
00222 g.add_edge(pydot.Edge(pedobj.pedigree[int(_m.sireID)-1].name, _m.name, dir='none'))
00223 else:
00224 if garrow:
00225 g.add_edge(pydot.Edge(pedobj.pedigree[int(_m.sireID)-1].originalID,_m.originalID))
00226 else:
00227 g.add_edge(pydot.Edge(pedobj.pedigree[int(_m.sireID)-1].originalID,_m.originalID, dir='none'))
00228 if _m.damID != pedobj.kw['missing_parent']:
00229 if gname:
00230 if garrow:
00231 g.add_edge(pydot.Edge(pedobj.pedigree[int(_m.damID)-1].name, _m.name))
00232 else:
00233 g.add_edge(pydot.Edge(pedobj.pedigree[int(_m.damID)-1].name, _m.name, dir='none'))
00234 else:
00235 if garrow:
00236 g.add_edge(pydot.Edge(pedobj.pedigree[int(_m.damID)-1].originalID,_m.originalID))
00237 else:
00238 g.add_edge(pydot.Edge(pedobj.pedigree[int(_m.damID)-1].originalID,_m.originalID, dir='none'))
00239
00240 else:
00241 for _g in gens:
00242 print '\t[DEBUG]: Looping over generations'
00243 _sg_anims = []
00244 _sg_name = 'sg%s' % (_g)
00245 sg = pydot.Subgraph(graph_name=_sg_name, suppress_disconnected=True, simplify=True)
00246 sg.set_simplify(True)
00247 animalCounter = 0
00248 for _m in pedobj.pedigree:
00249 animalCounter = animalCounter + 1
00250 if numpy.fmod(animalCounter,pedobj.kw['counter']) == 0:
00251 print '\t[DEBUG]: Records read: %s ' % ( animalCounter )
00252 if int(_m.gen) == int(_g):
00253 _sg_anims.append(_m.animalID)
00254
00255 if gname:
00256 _node_name = _m.name
00257 else:
00258 _node_name = _m.animalID
00259 _an_node = pydot.Node(_node_name)
00260 _an_node.set_fontname('Helvetica')
00261 _an_node.set_fontsize(gfontsize)
00262 _an_node.set_height('0.35')
00263 if _m.sex == 'M' or _m.sex == 'm':
00264 _an_node.set_shape('box')
00265 elif _m.sex == 'F' or _m.sex == 'f':
00266 _an_node.set_shape('ellipse')
00267 else:
00268 pass
00269 if _m.userField == ghatch:
00270 _an_node.set_style('filled,peripheries=2')
00271 else:
00272 _an_node.set_style('filled')
00273 _color = get_color_32(shading[_m.animalID], colormin, colormax)
00274 _an_node.set_fillcolor(_color)
00275 sg.add_node(_an_node)
00276
00277 if _m.sireID != pedobj.kw['missing_parent']:
00278 if gname:
00279 if garrow:
00280 sg.add_edge(pydot.Edge(pedobj.pedigree[int(_m.sireID)-1].name,_m.name))
00281 else:
00282 sg.add_edge(pydot.Edge(pedobj.pedigree[int(_m.sireID)-1].name,_m.name, dir='none'))
00283 else:
00284 if garrow:
00285 sg.add_edge(pydot.Edge(pedobj.pedigree[int(_m.sireID)-1].originalID,_m.originalID))
00286 else:
00287 sg.add_edge(pydot.Edge(pedobj.pedigree[int(_m.sireID)-1].originalID,_m.originalID, dir='none'))
00288
00289 if _m.damID != pedobj.kw['missing_parent']:
00290 if gname:
00291 if garrow:
00292 sg.add_edge(pydot.Edge(pedobj.pedigree[int(_m.damID)-1].name,_m.name))
00293 else:
00294 sg.add_edge(pydot.Edge(pedobj.pedigree[int(_m.damID)-1].name,_m.name, dir='none'))
00295 else:
00296 if garrow:
00297 sg.add_edge(pydot.Edge(pedobj.pedigree[int(_m.damID)-1].originalID,_m.originalID))
00298 else:
00299 sg.add_edge(pydot.Edge(pedobj.pedigree[int(_m.damID)-1].originalID,_m.originalID, dir='none'))
00300 if len(_sg_anims) > 0:
00301 _sg_list = ''
00302 for _a in _sg_anims:
00303 if len(_sg_list) == 0:
00304 _sg_list = 'same,%s' % (_a)
00305 else:
00306 _sg_list = '%s,%s' % (_sg_list,_a)
00307 sg.set_rank(_sg_list)
00308 g.add_subgraph(sg)
00309
00310
00311 if gdot:
00312 dfn = '%s.dot' % (gfilename)
00313
00314 g.write(dfn)
00315
00316
00317
00318 outfile = '%s.%s' % (gfilename,gformat)
00319 if gprog not in ['dot','neato','none']:
00320 gprog = 'dot'
00321 if gprog != 'none':
00322 g.write(outfile,prog=gprog,format=gformat)
00323 return 1
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351 def new_draw_colored_pedigree(pedobj, shading, gfilename='pedigree', \
00352 gtitle='', gformat='jpg', gsize='f', gdot=1, gorient='p', gdirec='', \
00353 gname=0, garrow=1, gtitloc='b', gtitjust='c', gshowall=1, gprog='dot', \
00354 ghatch='hatch'):
00355 """
00356 draw_pedigree() uses the pydot bindings to the graphviz library -- if they
00357 are available on your system -- to produce a directed graph of your pedigree
00358 with paths of inheritance as edges and animals as nodes. If there is more than
00359 one generation in the pedigree as determind by the "gen" attributes of the animals
00360 in the pedigree, draw_pedigree() will use subgraphs to try and group animals in the
00361 same generation together in the drawing.
00362 """
00363
00364 try:
00365 import pygraphviz
00366 except ImportError:
00367 if pedobj.kw['messages'] == 'verbose':
00368 print '[ERROR]: pyp_graphics/new_draw_pedigree() was unable to import the pygraphviz module!'
00369 logging.error('pyp_graphics/new_draw_pedigree() was unable to import the pygraphviz module!')
00370 return 0
00371
00372
00373
00374 _tf = {0:False, 1:True}
00375
00376 from pyp_utils import string_to_table_name
00377 _gtitle = string_to_table_name(gtitle)
00378
00379 if gtitloc not in ['t','b']:
00380 gtitloc = 'b'
00381 if gtitjust not in ['c','l','r']:
00382 gtitjust = 'c'
00383
00384 if not pedobj.kw['pedigree_is_renumbered']:
00385 if pedobj.kw['messages'] != 'quiet':
00386 print '[GRAPH]: The pedigree that you passed to pyp_graphics/draw_pedigree() is not renumbered. Because of this, there may be errors in the rendered pedigree. In order to insure that the pedigree drawing is accurate, you should renumber the pedigree before calling draw_pedigree().'
00387 logging.error('The pedigree that you passed to pyp_graphics/draw_pedigree() is not renumbered. Because of this, there may be errors in the rendered pedigree. In order to insure that the pedigree drawing is accurate, you should renumber the pedigree before calling draw_pedigree().')
00388
00389
00390 g = pygraphviz.AGraph(directed=True,strict=False)
00391
00392
00393 g.graph_attr['type'] = 'graph'
00394
00395
00396
00397 g.graph_attr['name'] = _gtitle
00398
00399
00400
00401
00402
00403 if gtitle != '':
00404 g.graph_attr['label'] = gtitle
00405 g.graph_attr['labelloc'] = gtitloc
00406 g.graph_attr['labeljust'] = gtitjust
00407
00408
00409 g.graph_attr['page'] = '8.5,11'
00410 g.graph_attr['size'] = '7.5,10'
00411
00412
00413 if gorient == 'l':
00414 g.graph_attr['orientation'] = 'landscape'
00415 else:
00416 g.graph_attr['orientation'] = 'portrait'
00417
00418 if gsize != 'l':
00419 g.graph_attr['ratio'] = 'auto'
00420 if gdirec == 'RL':
00421 g.graph_attr['rankdir'] = 'RL'
00422 elif gdirec == 'LR':
00423 g.graph_attr['rankdir'] = 'LR'
00424 else:
00425 pass
00426
00427
00428 g.graph_attr['center'] = 'True'
00429 g.graph_attr['concentrate'] = 'True'
00430 g.graph_attr['fontsize'] = str(pedobj.kw['default_fontsize'])
00431 g.graph_attr['ordering'] = 'out'
00432
00433
00434 colormin = min(shading.values())
00435 colormax = max(shading.values())
00436 color_map = {}
00437
00438 for _m in pedobj.pedigree:
00439
00440 if gname:
00441 _node_name = _m.name
00442 else:
00443 _node_name = _m.animalID
00444 g.add_node(_node_name)
00445 n = g.get_node(_node_name)
00446 n.attr['shape'] = 'box'
00447 n.attr['fontname'] = 'Helvetica'
00448 n.attr['fontsize'] = str(pedobj.kw['default_fontsize'])
00449 n.attr['height'] = '0.35'
00450
00451 if _m.sex == 'M' or _m.sex == 'm':
00452 n.attr['shape'] = 'box'
00453 elif _m.sex == 'F' or _m.sex == 'f':
00454 n.attr['shape'] = 'ellipse'
00455 else:
00456 n.attr['shape'] = 'octagon'
00457
00458
00459
00460 if _m.userField == ghatch:
00461 n.attr['style'] = 'filled,peripheries=2'
00462 else:
00463 n.attr['style'] = 'filled'
00464 _color = get_color_32(shading[_m.animalID], colormin, colormax)
00465 n.attr['fillcolor'] = _color
00466
00467 if not color_map.has_key(shading[_m.animalID]):
00468 color_map[shading[_m.animalID]] = _color
00469
00470
00471 if int(_m.sireID) != pedobj.kw['missing_parent']:
00472 if gname:
00473 _sire_edge = pedobj.pedigree[int(_m.sireID)-1].name
00474 else:
00475
00476
00477
00478
00479 _sire_edge = pedobj.pedigree[int(_m.sireID)-1].animalID
00480 g.add_edge(_sire_edge,_node_name)
00481 if not _tf[garrow]:
00482 e = g.get_edge(_sire_edge,_anim_node)
00483 e.attr['dir'] = 'none'
00484 if int(_m.damID) != pedobj.kw['missing_parent']:
00485 if gname:
00486 _dam_edge = pedobj.pedigree[int(_m.damID)-1].name
00487 else:
00488 _dam_edge = pedobj.pedigree[int(_m.damID)-1].animalID
00489 g.add_edge(_dam_edge,_node_name)
00490 if not _tf[garrow]:
00491 e = g.get_edge(_dam_edge,_anim_node)
00492 e.attr['dir'] = 'none'
00493
00494
00495
00496
00497 if gdot:
00498 dfn = '%s.dot' % (gfilename)
00499 try:
00500 g.write(dfn)
00501 except:
00502 if pedobj.kw['messages'] == 'verbose':
00503 print '[ERROR]: pyp_graphics/new_draw_pedigree() was unable to write the dotfile %s.' % (dfn)
00504 logging.error('pyp_graphics/new_draw_pedigree() was unable to draw the dotfile %s.', (dfn))
00505
00506
00507
00508
00509 mapfile = '%s_color_map.txt' % (gfilename)
00510 mf = file(mapfile,'w')
00511 mf.write('# Color map data\n')
00512 mf.write('# Data are metric (number of sons/descendants/etc.) followed\n')
00513 mf.write('# by color in RGB.\n')
00514 for k,v in color_map.iteritems():
00515 line = '%s\t%s\n' % (k,v)
00516 mf.write(line)
00517 mf.close()
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527 outfile = '%s.%s' % (gfilename,gformat)
00528 g.draw(outfile,prog=gprog)
00529 return 1
00530
00531
00532
00533
00534
00535