Skip to content

Commit

Permalink
Add support for strand information in bed tracks
Browse files Browse the repository at this point in the history
  • Loading branch information
e-sollier committed Sep 23, 2024
1 parent fcc3245 commit 519d662
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 4 deletions.
2 changes: 2 additions & 0 deletions docs/content/describe_figure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ Parameters:

* ``show_names``: if True, will display the name of each region above the corresponding rectangle. This requires that the bed file contains a fourth column indicating the name of each region.

* ``show_strand``: if True, will display arrows indicating the strand of each region. This requires that the bed file contains a sixth column indicating the strand of each region ("+", "-", or ".").

bigwig
^^^^^^^^

Expand Down
4 changes: 4 additions & 0 deletions figeno/gui/src/Track.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,10 @@ function BedTrack({track,set_value,openColorPanel, setFileDialogData,setFileDial
<label htmlFor={"show_names"+track.id}>Show names:</label>
<input type="checkbox" id={"show_names"+track.id} checked={track.show_names} onChange={() => set_value("show_names",!track.show_names)} ></input>
</div>
<div className='formItem'>
<label htmlFor={"show_strand"+track.id}>Show strand:</label>
<input type="checkbox" id={"show_strand"+track.id} checked={track.show_strand} onChange={() => set_value("show_strand",!track.show_strand)} ></input>
</div>

</div>
</>
Expand Down
3 changes: 2 additions & 1 deletion figeno/gui/src/TracksContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,14 @@ export const defaultTrackValues={
show_gene_names:true
},
"bed":{
height:"10",
height:"3",
margin_above:"1.5",
bounding_box:false,
fontscale:1.0,
file:"",
color:"#444444",
show_names:false,
show_strand:false,
label:"",
label_rotate:false
},
Expand Down
50 changes: 47 additions & 3 deletions figeno/track_bed.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@


class bed_track:
def __init__(self,file,color="#051650",label="",label_rotate=False,show_names=False,fontscale=1,bounding_box=False,height=6,margin_above=1.5,**kwargs):
def __init__(self,file,color="#051650",label="",label_rotate=False,show_names=False,show_strand=False,fontscale=1,bounding_box=False,height=3,margin_above=1.5,**kwargs):
self.file = file
self.color=color
self.label=label
self.label_rotate=label_rotate
self.show_names = show_names
self.show_strand=show_strand
self.fontscale=float(fontscale)
self.bounding_box=bounding_box
self.height = float(height)
Expand All @@ -27,6 +28,13 @@ def draw(self, regions, box ,hmargin,warnings):
if not os.path.isfile(self.file):
raise KnownException("The following bed file could not be found: "+self.file)

# Make sure that the file can be read
try:
with open(self.file,"r") as infile:
line=infile.readline()
except:
raise KnownException("The following bed file could not be read: "+self.file)

boxes = split_box(box,regions,hmargin)
for i in range(len(regions)):
self.draw_region(regions[i][0],boxes[i],warnings)
Expand All @@ -37,7 +45,6 @@ def draw(self, regions, box ,hmargin,warnings):

def draw_region(self,region,box,warnings):
if self.bounding_box: draw_bounding_box(box)


if self.show_names:
rect_height = (box["top"]-box["bottom"]) * 0.4
Expand All @@ -46,8 +53,15 @@ def draw_region(self,region,box,warnings):
rect_height = (box["top"]-box["bottom"]) * 0.7
rect_bottom = box["bottom"] + (box["top"]-box["bottom"]) * 0.15

# Params for strand
self.arrow_gapwidth=3 # gap between arrows
self.arrow_height=min(0.8*rect_height,1.4)
self.arrow_width=min(0.5,1.5*self.arrow_height)


with open(self.file,"r") as infile:
for line in infile:
if line.startswith("#"): continue
linesplit = line.rstrip("\n").split("\t")
if len(linesplit)<3: raise KnownException("The following bed file has the wrong format: "+self.file+".\nThe file should be tab-separated, without header, with at least 3 columns: chr, start, end.\nProblematic line:\n"+line[:70])
if linesplit[0].lstrip("chr")==region.chr.lstrip("chr"):
Expand All @@ -64,16 +78,46 @@ def draw_region(self,region,box,warnings):
converted_end = box["right"] - (start-region.start) / (region.end-region.start) * (box["right"] - box["left"])
converted_start = min(box["right"],max(box["left"],converted_start))
converted_end = min(box["right"],max(box["left"],converted_end))
rect_width=converted_end-converted_start

rect = patches.Rectangle((converted_start,rect_bottom),converted_end-converted_start,rect_height,color=self.color)
rect = patches.Rectangle((converted_start,rect_bottom),rect_width,rect_height,color=self.color)
box["ax"].add_patch(rect)


if self.show_names:
if len(linesplit)>3:
box["ax"].text((converted_start+converted_end)/2,(rect_bottom+rect_height+1),linesplit[3],
horizontalalignment="center",verticalalignment="bottom",fontsize=8*self.fontscale)
else:
warnings.append("For the bed file "+self.file+", you ticked the show_names option, but no names were found for line "+line +" (should be the 4th column).")


if self.show_strand:
arrow_center=rect_bottom+rect_height/2
arrow_bottom=arrow_center-self.arrow_height/2
arrow_top=arrow_center+self.arrow_height/2
if len(linesplit)>5:
strand=linesplit[5]
if not strand in ["+","-"]: continue
if rect_width<1.2*self.arrow_width: continue
# Number of arrows that can fit in the rectangle
n_arrows= 1+ int((rect_width-3*self.arrow_width)/(self.arrow_gapwidth+self.arrow_width))

if rect_width<3*self.arrow_width:
current_left_pos=(converted_start+converted_end)/2 - self.arrow_width/2
else:
arrows_width= 3*self.arrow_width + (n_arrows-1)*(self.arrow_gapwidth+self.arrow_width)
current_left_pos=(converted_start+converted_end)/2 -arrows_width/2 + self.arrow_width
for i in range(n_arrows):
if strand=="+":
box["ax"].plot([current_left_pos,current_left_pos+self.arrow_width,current_left_pos],[arrow_top,arrow_center,arrow_bottom],color="white",lw=0.4,zorder=3)
else:
box["ax"].plot([current_left_pos+self.arrow_width,current_left_pos,current_left_pos+self.arrow_width],[arrow_top,arrow_center,arrow_bottom],color="white",lw=0.4,zorder=3)
current_left_pos+=self.arrow_gapwidth+self.arrow_width
else:
warnings.append("For the bed file "+self.file+", you ticked the show_strand option, but no strand information was found for line "+line +" (should be the 6th column).")


def draw_title(self,box):
if len(self.label)>0:
self.label = self.label.replace("\\n","\n")
Expand Down

0 comments on commit 519d662

Please sign in to comment.