Package Bio :: Package Graphics :: Module BasicChromosome
[hide private]
[frames] | no frames]

Source Code for Module Bio.Graphics.BasicChromosome

  1  """Draw representations of organism chromosomes with added information. 
  2   
  3  These classes are meant to model the drawing of pictures of chromosomes. 
  4  This can be useful for lots of things, including displaying markers on 
  5  a chromosome (ie. for genetic mapping) and showing syteny between two 
  6  chromosomes. 
  7   
  8  The structure of these classes is intended to be a Composite, so that 
  9  it will be easy to plug in and switch different parts without 
 10  breaking the general drawing capabilities of the system. The 
 11  relationship between classes is that everything derives from 
 12  _ChromosomeComponent, which specifies the overall interface. The parts 
 13  then are related so that an Organism contains Chromosomes, and these 
 14  Chromosomes contain ChromosomeSegments. This representation differents 
 15  from the canonical composite structure in that we don't really have 
 16  'leaf' nodes here -- all components can potentially hold sub-components. 
 17   
 18  Most of the time the ChromosomeSegment class is what you'll want to 
 19  customize for specific drawing tasks. 
 20   
 21  For providing drawing capabilities, these classes use reportlab: 
 22   
 23  http://www.reportlab.com 
 24   
 25  This provides nice output in PDF, SVG and postscript.  If you have 
 26  reportlab's renderPM module installed you can also use PNG etc. 
 27  """ 
 28  # standard library 
 29  import os 
 30   
 31  # reportlab 
 32  from reportlab.pdfgen import canvas 
 33  from reportlab.lib.pagesizes import letter 
 34  from reportlab.lib.units import inch 
 35  from reportlab.lib import colors 
 36   
 37  from reportlab.graphics.shapes import Drawing, String, Line, Rect, Wedge, ArcPath 
 38  from reportlab.graphics import renderPDF, renderPS 
 39  from reportlab.graphics.widgetbase import Widget 
 40   
 41  from Bio.Graphics import _write 
 42  from Bio.Graphics.GenomeDiagram._Colors import ColorTranslator as _ColorTranslator 
 43   
 44  _color_trans = _ColorTranslator() 
 45   
 46   
47 -class _ChromosomeComponent(Widget):
48 """Base class specifying the interface for a component of the system. 49 50 This class should not be instantiated directly, but should be used 51 from derived classes. 52 """
53 - def __init__(self):
54 """Initialize a chromosome component. 55 56 Attributes: 57 58 o _sub_components -- Any components which are contained under 59 this parent component. This attribute should be accessed through 60 the add() and remove() functions. 61 """ 62 self._sub_components = []
63
64 - def add(self, component):
65 """Add a sub_component to the list of components under this item. 66 """ 67 assert isinstance(component, _ChromosomeComponent), \ 68 "Expected a _ChromosomeComponent object, got %s" % component 69 70 self._sub_components.append(component)
71
72 - def remove(self, component):
73 """Remove the specified component from the subcomponents. 74 75 Raises a ValueError if the component is not registered as a 76 sub_component. 77 """ 78 try: 79 self._sub_components.remove(component) 80 except ValueError: 81 raise ValueError("Component %s not found in sub_components." % 82 component)
83
84 - def draw(self):
85 """Draw the specified component. 86 """ 87 raise AssertionError("Subclasses must implement.")
88 89
90 -class Organism(_ChromosomeComponent):
91 """Top level class for drawing chromosomes. 92 93 This class holds information about an organism and all of it's 94 chromosomes, and provides the top level object which could be used 95 for drawing a chromosome representation of an organism. 96 97 Chromosomes should be added and removed from the Organism via the 98 add and remove functions. 99 """
100 - def __init__(self, output_format = 'pdf'):
101 _ChromosomeComponent.__init__(self) 102 103 # customizable attributes 104 self.page_size = letter 105 self.title_size = 20 106 107 #Do we need this given we don't draw a legend? 108 #If so, should be a public API... 109 self._legend_height = 0 # 2 * inch 110 111 self.output_format = output_format
112
113 - def draw(self, output_file, title):
114 """Draw out the information for the Organism. 115 116 Arguments: 117 118 o output_file -- The name of a file specifying where the 119 document should be saved, or a handle to be written to. 120 The output format is set when creating the Organism object. 121 122 o title -- The output title of the produced document. 123 """ 124 width, height = self.page_size 125 cur_drawing = Drawing(width, height) 126 127 self._draw_title(cur_drawing, title, width, height) 128 129 cur_x_pos = inch * .5 130 if len(self._sub_components) > 0: 131 x_pos_change = (width - inch) / len(self._sub_components) 132 # no sub_components 133 else: 134 pass 135 136 for sub_component in self._sub_components: 137 # set the drawing location of the chromosome 138 sub_component.start_x_position = cur_x_pos + 0.05 * x_pos_change 139 sub_component.end_x_position = cur_x_pos + 0.95 * x_pos_change 140 sub_component.start_y_position = height - 1.5 * inch 141 sub_component.end_y_position = self._legend_height + 1 * inch 142 143 # do the drawing 144 sub_component.draw(cur_drawing) 145 146 # update the locations for the next chromosome 147 cur_x_pos += x_pos_change 148 149 self._draw_legend(cur_drawing, self._legend_height + 0.5 * inch, width) 150 151 return _write(cur_drawing, output_file, self.output_format)
152
153 - def _draw_title(self, cur_drawing, title, width, height):
154 """Write out the title of the organism figure. 155 """ 156 title_string = String(width / 2, height - inch, title) 157 title_string.fontName = 'Helvetica-Bold' 158 title_string.fontSize = self.title_size 159 title_string.textAnchor = "middle" 160 161 cur_drawing.add(title_string)
162
163 - def _draw_legend(self, cur_drawing, start_y, width):
164 """Draw a legend for the figure. 165 166 Subclasses should implement this (see also self._legend_height) to 167 provide specialized legends. 168 """ 169 pass
170 171
172 -class Chromosome(_ChromosomeComponent):
173 """Class for drawing a chromosome of an organism. 174 175 This organizes the drawing of a single organisms chromosome. This 176 class can be instantiated directly, but the draw method makes the 177 most sense to be called in the context of an organism. 178 """
179 - def __init__(self, chromosome_name):
180 """Initialize a Chromosome for drawing. 181 182 Arguments: 183 184 o chromosome_name - The label for the chromosome. 185 186 Attributes: 187 188 o start_x_position, end_x_position - The x positions on the page 189 where the chromosome should be drawn. This allows multiple 190 chromosomes to be drawn on a single page. 191 192 o start_y_position, end_y_position - The y positions on the page 193 where the chromosome should be contained. 194 195 Configuration Attributes: 196 197 o title_size - The size of the chromosome title. 198 199 o scale_num - A number of scale the drawing by. This is useful if 200 you want to draw multiple chromosomes of different sizes at the 201 same scale. If this is not set, then the chromosome drawing will 202 be scaled by the number of segements in the chromosome (so each 203 chromosome will be the exact same final size). 204 """ 205 _ChromosomeComponent.__init__(self) 206 207 self._name = chromosome_name 208 209 self.start_x_position = -1 210 self.end_x_position = -1 211 self.start_y_position = -1 212 self.end_y_position = -1 213 214 self.title_size = 20 215 self.scale_num = None 216 217 self.label_size = 6 218 self.chr_percent = 0.25 219 self.label_sep_percent = self.chr_percent * 0.5 220 self._color_labels = False
221
222 - def subcomponent_size(self):
223 """Return the scaled size of all subcomponents of this component. 224 """ 225 total_sub = 0 226 for sub_component in self._sub_components: 227 total_sub += sub_component.scale 228 229 return total_sub
230
231 - def draw(self, cur_drawing):
232 """Draw a chromosome on the specified template. 233 234 Ideally, the x_position and y_*_position attributes should be 235 set prior to drawing -- otherwise we're going to have some problems. 236 """ 237 for position in (self.start_x_position, self.end_x_position, 238 self.start_y_position, self.end_y_position): 239 assert position != -1, "Need to set drawing coordinates." 240 241 # first draw all of the sub-sections of the chromosome -- this 242 # will actually be the picture of the chromosome 243 cur_y_pos = self.start_y_position 244 if self.scale_num: 245 y_pos_change = ((self.start_y_position * .95 - self.end_y_position) 246 / self.scale_num) 247 elif len(self._sub_components) > 0: 248 y_pos_change = ((self.start_y_position * .95 - self.end_y_position) 249 / self.subcomponent_size()) 250 # no sub_components to draw 251 else: 252 pass 253 254 left_labels = [] 255 right_labels = [] 256 for sub_component in self._sub_components: 257 this_y_pos_change = sub_component.scale * y_pos_change 258 259 # set the location of the component to draw 260 sub_component.start_x_position = self.start_x_position 261 sub_component.end_x_position = self.end_x_position 262 sub_component.start_y_position = cur_y_pos 263 sub_component.end_y_position = cur_y_pos - this_y_pos_change 264 265 # draw the sub component 266 sub_component._left_labels = [] 267 sub_component._right_labels = [] 268 sub_component.draw(cur_drawing) 269 left_labels += sub_component._left_labels 270 right_labels += sub_component._right_labels 271 272 # update the position for the next component 273 cur_y_pos -= this_y_pos_change 274 275 self._draw_labels(cur_drawing, left_labels, right_labels) 276 self._draw_label(cur_drawing, self._name)
277
278 - def _draw_label(self, cur_drawing, label_name):
279 """Draw a label for the chromosome. 280 """ 281 x_position = 0.5 * (self.start_x_position + self.end_x_position) 282 y_position = self.end_y_position 283 284 label_string = String(x_position, y_position, label_name) 285 label_string.fontName = 'Times-BoldItalic' 286 label_string.fontSize = self.title_size 287 label_string.textAnchor = 'middle' 288 289 cur_drawing.add(label_string)
290
291 - def _draw_labels(self, cur_drawing, left_labels, right_labels):
292 """Layout and draw sub-feature labels for the chromosome. 293 294 Tries to place each label at the same vertical position as the 295 feature it applies to, but will adjust the positions to avoid or 296 at least reduce label overlap. 297 298 Draws the label text and a coloured line linking it to the 299 location (i.e. feature) it applies to. 300 """ 301 if not self._sub_components: 302 return 303 color_label = self._color_labels 304 305 segment_width = (self.end_x_position - self.start_x_position) \ 306 * self.chr_percent 307 label_sep = (self.end_x_position - self.start_x_position) \ 308 * self.label_sep_percent 309 segment_x = self.start_x_position \ 310 + 0.5 * (self.end_x_position - self.start_x_position - segment_width) 311 312 y_limits = [] 313 for sub_component in self._sub_components: 314 y_limits.extend((sub_component.start_y_position, sub_component.end_y_position)) 315 y_min = min(y_limits) 316 y_max = max(y_limits) 317 del y_limits 318 #Now do some label placement magic... 319 #from reportlab.pdfbase import pdfmetrics 320 #font = pdfmetrics.getFont('Helvetica') 321 #h = (font.face.ascent + font.face.descent) * 0.90 322 h = self.label_size 323 left_labels = _place_labels(left_labels, y_min, y_max, h) 324 right_labels = _place_labels(right_labels, y_min, y_max, h) 325 x1 = segment_x 326 x2 = segment_x - label_sep 327 for (y1, y2, color, name) in left_labels: 328 cur_drawing.add(Line(x1, y1, x2, y2, 329 strokeColor = color, 330 strokeWidth = 0.25)) 331 label_string = String(x2, y2, name, 332 textAnchor="end") 333 label_string.fontName = 'Helvetica' 334 label_string.fontSize = self.label_size 335 if color_label: 336 label_string.fillColor = color 337 cur_drawing.add(label_string) 338 x1 = segment_x + segment_width 339 x2 = segment_x + segment_width + label_sep 340 for (y1, y2, color, name) in right_labels: 341 cur_drawing.add(Line(x1, y1, x2, y2, 342 strokeColor = color, 343 strokeWidth = 0.25)) 344 label_string = String(x2, y2, name) 345 label_string.fontName = 'Helvetica' 346 label_string.fontSize = self.label_size 347 if color_label: 348 label_string.fillColor = color 349 cur_drawing.add(label_string)
350 351
352 -class ChromosomeSegment(_ChromosomeComponent):
353 """Draw a segment of a chromosome. 354 355 This class provides the important configurable functionality of drawing 356 a Chromosome. Each segment has some customization available here, or can 357 be subclassed to define additional functionality. Most of the interesting 358 drawing stuff is likely to happen at the ChromosomeSegment level. 359 """
360 - def __init__(self):
361 """Initialize a ChromosomeSegment. 362 363 Attributes: 364 o start_x_position, end_x_position - Defines the x range we have 365 to draw things in. 366 367 o start_y_position, end_y_position - Defines the y range we have 368 to draw things in. 369 370 Configuration Attributes: 371 372 o scale - A scaling value for the component. By default this is 373 set at 1 (ie -- has the same scale as everything else). Higher 374 values give more size to the component, smaller values give less. 375 376 o fill_color - A color to fill in the segment with. Colors are 377 available in reportlab.lib.colors 378 379 o label - A label to place on the chromosome segment. This should 380 be a text string specifying what is to be included in the label. 381 382 o label_size - The size of the label. 383 384 o chr_percent - The percentage of area that the chromosome 385 segment takes up. 386 """ 387 _ChromosomeComponent.__init__(self) 388 389 self.start_x_position = -1 390 self.end_x_position = -1 391 self.start_y_position = -1 392 self.end_y_position = -1 393 394 # --- attributes for configuration 395 self.scale = 1 396 self.fill_color = None 397 self.label = None 398 self.label_size = 6 399 self.chr_percent = .25
400
401 - def draw(self, cur_drawing):
402 """Draw a chromosome segment. 403 404 Before drawing, the range we are drawing in needs to be set. 405 """ 406 for position in (self.start_x_position, self.end_x_position, 407 self.start_y_position, self.end_y_position): 408 assert position != -1, "Need to set drawing coordinates." 409 410 self._draw_subcomponents(cur_drawing) # Anything behind 411 self._draw_segment(cur_drawing) 412 self._overdraw_subcomponents(cur_drawing) # Anything on top 413 self._draw_label(cur_drawing)
414
415 - def _draw_subcomponents(self, cur_drawing):
416 """Draw any subcomponents of the chromosome segment. 417 418 This should be overridden in derived classes if there are 419 subcomponents to be drawn. 420 """ 421 pass
422
423 - def _draw_segment(self, cur_drawing):
424 """Draw the current chromosome segment. 425 """ 426 # set the coordinates of the segment -- it'll take up the MIDDLE part 427 # of the space we have. 428 segment_y = self.end_y_position 429 segment_width = (self.end_x_position - self.start_x_position) \ 430 * self.chr_percent 431 segment_height = self.start_y_position - self.end_y_position 432 segment_x = self.start_x_position \ 433 + 0.5 * (self.end_x_position - self.start_x_position - segment_width) 434 435 # first draw the sides of the segment 436 right_line = Line(segment_x, segment_y, 437 segment_x, segment_y + segment_height) 438 left_line = Line(segment_x + segment_width, segment_y, 439 segment_x + segment_width, segment_y + segment_height) 440 441 cur_drawing.add(right_line) 442 cur_drawing.add(left_line) 443 444 # now draw the box, if it is filled in 445 if self.fill_color is not None: 446 fill_rectangle = Rect(segment_x, segment_y, 447 segment_width, segment_height) 448 fill_rectangle.fillColor = self.fill_color 449 fill_rectangle.strokeColor = None 450 451 cur_drawing.add(fill_rectangle)
452
453 - def _overdraw_subcomponents(self, cur_drawing):
454 """Draw any subcomponents of the chromosome segment over the main part. 455 456 This should be overridden in derived classes if there are 457 subcomponents to be drawn. 458 """ 459 pass
460
461 - def _draw_label(self, cur_drawing):
462 """Add a label to the chromosome segment. 463 464 The label will be applied to the right of the segment. 465 466 This may be overlapped by any sub-feature labels on other segments! 467 """ 468 if self.label is not None: 469 470 label_x = 0.5 * (self.start_x_position + self.end_x_position) + \ 471 (self.chr_percent + 0.05) * (self.end_x_position - 472 self.start_x_position) 473 label_y = ((self.start_y_position - self.end_y_position) / 2 + 474 self.end_y_position) 475 476 label_string = String(label_x, label_y, self.label) 477 label_string.fontName = 'Helvetica' 478 label_string.fontSize = self.label_size 479 480 cur_drawing.add(label_string)
481 482
483 -def _spring_layout(desired, minimum, maximum, gap=0):
484 """Function to try and layout label co-ordinates (or other floats, PRIVATE). 485 486 Originally written for the y-axis vertical positioning of labels on a 487 chromosome diagram (where the minimum gap between y-axis co-ordinates is 488 the label height), it could also potentially be used for x-axis placement, 489 or indeed radial placement for circular chromosomes within GenomeDiagram. 490 491 In essence this is an optimisation problem, balancing the desire to have 492 each label as close as possible to its data point, but also to spread out 493 the labels to avoid overlaps. This could be described with a cost function 494 (modelling the label distance from the desired placement, and the inter- 495 label separations as springs) and solved as a multi-variable minimization 496 problem - perhaps with NumPy or SciPy. 497 498 For now however, the implementation is a somewhat crude ad hoc algorithm. 499 500 NOTE - This expects the input data to have been sorted! 501 """ 502 count = len(desired) 503 if count <= 1: 504 return desired # Easy! 505 if minimum >= maximum: 506 raise ValueError("Bad min/max %f and %f" % (minimum, maximum)) 507 if min(desired) < minimum or max(desired) > maximum: 508 raise ValueError("Data %f to %f out of bounds (%f to %f)" 509 % (min(desired), max(desired), minimum, maximum)) 510 equal_step = float(maximum - minimum) / (count - 1) 511 512 if equal_step < gap: 513 import warnings 514 from Bio import BiopythonWarning 515 warnings.warn("Too many labels to avoid overlap", BiopythonWarning) 516 #Crudest solution 517 return [minimum+i*equal_step for i in range(count)] 518 519 good = True 520 if gap: 521 prev = desired[0] 522 for next in desired[1:]: 523 if prev - next < gap: 524 good = False 525 break 526 if good: 527 return desired 528 529 span = maximum - minimum 530 for split in [0.5*span, span/3.0, 2*span/3.0, 0.25*span, 0.75*span]: 531 midpoint = minimum + split 532 low = [x for x in desired if x <= midpoint - 0.5*gap] 533 high = [x for x in desired if x > midpoint + 0.5*gap] 534 if len(low)+len(high) < count: 535 #Bad split point, points right on boundary 536 continue 537 elif not low and len(high)*gap <= (span-split) + 0.5*gap: 538 #Give a little of the unused low space to the high points 539 return _spring_layout(high, midpoint + 0.5*gap, maximum, gap) 540 elif not high and len(low)*gap <= split + 0.5*gap: 541 #Give a little of the unused highspace to the low points 542 return _spring_layout(low, minimum, midpoint - 0.5*gap, gap) 543 elif len(low)*gap <= split - 0.5*gap \ 544 and len(high)*gap <= (span-split) - 0.5*gap: 545 return _spring_layout(low, minimum, midpoint - 0.5*gap, gap) + \ 546 _spring_layout(high, midpoint+ 0.5*gap, maximum, gap) 547 548 #This can be count-productive now we can split out into the telomere or 549 #spacer-segment's vertical space... 550 #Try not to spread out as far as the min/max unless needed 551 low = min(desired) 552 high = max(desired) 553 if (high-low) / (count-1) >= gap: 554 #Good, we don't need the full range, and can position the 555 #min and max exactly as well :) 556 equal_step = (high-low) / (count-1) 557 return [low+i*equal_step for i in range(count)] 558 559 low = 0.5 * (minimum + min(desired)) 560 high = 0.5 * (max(desired) + maximum) 561 if (high-low) / (count-1) >= gap: 562 #Good, we don't need the full range 563 equal_step = (high-low) / (count-1) 564 return [low+i*equal_step for i in range(count)] 565 566 #Crudest solution 567 return [minimum+i*equal_step for i in range(count)]
568 569 #assert False, _spring_layout([0.10,0.12,0.13,0.14,0.5,0.75, 1.0], 0, 1, 0.1) 570 #assert _spring_layout([0.10,0.12,0.13,0.14,0.5,0.75, 1.0], 0, 1, 0.1) == [0.0, 0.125, 0.25, 0.375, 0.5, 0.75, 1.0] 571 #assert _spring_layout([0.10,0.12,0.13,0.14,0.5,0.75, 1.0], 0, 1, 0.1) == [0.0, 0.16666666666666666, 0.33333333333333331, 0.5, 0.66666666666666663, 0.83333333333333326, 1.0] 572 573
574 -def _place_labels(desired_etc, minimum, maximum, gap=0):
575 desired_etc.sort() 576 placed = _spring_layout([row[0] for row in desired_etc], 577 minimum, maximum, gap) 578 for old,y2 in zip(desired_etc, placed): 579 y1, color, name = old 580 yield (y1, y2, color, name)
581 582
583 -class AnnotatedChromosomeSegment(ChromosomeSegment):
584 - def __init__(self, bp_length, features, 585 default_feature_color=colors.blue, 586 name_qualifiers = ['gene', 'label', 'name', 'locus_tag', 'product']):
587 """Like the ChromosomeSegment, but accepts a list of features. 588 589 The features can either be SeqFeature objects, or tuples of five 590 values: start (int), end (int), strand (+1, -1, O or None), label 591 (string) and a ReportLab color. 592 593 Note we require 0 <= start <= end <= bp_length, and within the vertical 594 space allocated to this segmenet lines will be places according to the 595 start/end coordinates (starting from the top). 596 597 Positive stand features are drawn on the right, negative on the left, 598 otherwise all the way across. 599 600 We recommend using consisent units for all the segment's scale values 601 (e.g. their length in base pairs). 602 603 When providing features as SeqFeature objects, the default color 604 is used, unless the feature's qualifiers include an Artemis colour 605 string (functionality also in GenomeDiagram). The caption also follows 606 the GenomeDiagram approach and takes the first qualifier from the list 607 specified in name_qualifiers. 608 609 Note additional attribute label_sep_percent controls the percentage of 610 area that the chromosome segment takes up, by default half of the 611 chr_percent attribute (half of 25%, thus 12.5%) 612 613 """ 614 ChromosomeSegment.__init__(self) 615 self.bp_length = bp_length 616 self.features = features 617 self.default_feature_color = default_feature_color 618 self.name_qualifiers = name_qualifiers 619 self.label_sep_percent = self.chr_percent * 0.5
620
621 - def _overdraw_subcomponents(self, cur_drawing):
622 """Draw any annotated features on the chromosome segment. 623 624 Assumes _draw_segment already called to fill out the basic shape, 625 and assmes that uses the same boundaries. 626 """ 627 # set the coordinates of the segment -- it'll take up the MIDDLE part 628 # of the space we have. 629 segment_y = self.end_y_position 630 segment_width = (self.end_x_position - self.start_x_position) \ 631 * self.chr_percent 632 label_sep = (self.end_x_position - self.start_x_position) \ 633 * self.label_sep_percent 634 segment_height = self.start_y_position - self.end_y_position 635 segment_x = self.start_x_position \ 636 + 0.5 * (self.end_x_position - self.start_x_position - segment_width) 637 638 left_labels = [] 639 right_labels = [] 640 for f in self.features: 641 try: 642 #Assume SeqFeature objects 643 start = f.location.start 644 end = f.location.end 645 strand = f.strand 646 try: 647 #Mimic the GenomeDiagram code 648 color = _color_trans.artemis_color( 649 f.qualifiers['color'][0]) 650 except: 651 color = self.default_feature_color 652 name = "" 653 for qualifier in self.name_qualifiers: 654 if qualifier in f.qualifiers: 655 name = f.qualifiers[qualifier][0] 656 break 657 except AttributeError: 658 #Assume tuple of ints, string, and color 659 start, end, strand, name, color = f 660 assert 0 <= start <= end <= self.bp_length 661 if strand == +1 : 662 #Right side only 663 x = segment_x + segment_width * 0.6 664 w = segment_width * 0.4 665 elif strand == -1: 666 #Left side only 667 x = segment_x 668 w = segment_width * 0.4 669 else: 670 #Both or neighther - full width 671 x = segment_x 672 w = segment_width 673 local_scale = segment_height / self.bp_length 674 fill_rectangle = Rect(x, segment_y + segment_height - local_scale*start, 675 w, local_scale*(start-end)) 676 fill_rectangle.fillColor = color 677 fill_rectangle.strokeColor = color 678 cur_drawing.add(fill_rectangle) 679 if name: 680 value = (segment_y + segment_height - local_scale*start, color, name) 681 if strand == -1: 682 self._left_labels.append(value) 683 else: 684 self._right_labels.append(value)
685 686
687 -class TelomereSegment(ChromosomeSegment):
688 """A segment that is located at the end of a linear chromosome. 689 690 This is just like a regular segment, but it draws the end of a chromosome 691 which is represented by a half circle. This just overrides the 692 _draw_segment class of ChromosomeSegment to provide that specialized 693 drawing. 694 """
695 - def __init__(self, inverted = 0):
696 """Initialize a segment at the end of a chromosome. 697 698 See ChromosomeSegment for all of the attributes that can be 699 customized in a TelomereSegments. 700 701 Arguments: 702 703 o inverted -- Whether or not the telomere should be inverted 704 (ie. drawn on the bottom of a chromosome) 705 """ 706 ChromosomeSegment.__init__(self) 707 708 self._inverted = inverted
709
710 - def _draw_segment(self, cur_drawing):
711 """Draw a half circle representing the end of a linear chromosome. 712 """ 713 # set the coordinates of the segment -- it'll take up the MIDDLE part 714 # of the space we have. 715 width = (self.end_x_position - self.start_x_position) \ 716 * self.chr_percent 717 height = self.start_y_position - self.end_y_position 718 center_x = 0.5 * (self.end_x_position + self.start_x_position) 719 start_x = center_x - 0.5 * width 720 if self._inverted: 721 center_y = self.start_y_position 722 start_angle = 180 723 end_angle = 360 724 else: 725 center_y = self.end_y_position 726 start_angle = 0 727 end_angle = 180 728 729 cap_wedge = Wedge(center_x, center_y, width / 2, 730 start_angle, end_angle, height) 731 cap_wedge.strokeColor = None 732 cap_wedge.fillColor = self.fill_color 733 cur_drawing.add(cap_wedge) 734 735 #Now draw an arc for the curved edge of the wedge, 736 #omitting the flat end. 737 cap_arc = ArcPath() 738 cap_arc.addArc(center_x, center_y, width / 2, 739 start_angle, end_angle, height) 740 cur_drawing.add(cap_arc)
741 742
743 -class SpacerSegment(ChromosomeSegment):
744 """A segment that is located at the end of a linear chromosome. 745 746 Doesn't draw anything, just empty space which can be helpful 747 for layout purposes (e.g. making room for feature labels). 748 """ 749
750 - def draw(self, cur_diagram):
751 pass
752