Skip to content

[WIP] Arrows on links #455

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pygenometracks/tests/generateAllOutput.sh
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ bin/pgt --tracks ./pygenometracks/tests/test_data/arcs_no_score.ini --region X:3
bin/pgt --tracks ./pygenometracks/tests/test_data/links_squares.ini --region X:3000000-3300000 --trackLabelFraction 0.2 --width 38 --dpi 130 -o pygenometracks/tests/test_data/master_links_squares.png
bin/pgt --tracks ./pygenometracks/tests/test_data/arcs_overlay.ini --region X:3000000-3300000 --trackLabelFraction 0.2 --width 38 --dpi 130 -o pygenometracks/tests/test_data/master_arcs_overlay.png
bin/pgt --tracks ./pygenometracks/tests/test_data/links_squares_overlay.ini --region X:3000000-3300000 --trackLabelFraction 0.2 --width 38 --dpi 130 -o pygenometracks/tests/test_data/master_links_squares_overlay.png
bin/pgt --tracks ./pygenometracks/tests/test_data/short_long_arcs_plot_arrows.ini --region chr11:40000000-46000000 --trackLabelFraction 0.2 --width 38 --dpi 130 -o pygenometracks/tests/test_data/master_short_long_arcs_plot_arrows.png

# tests scaleBar
bin/pgt --tracks pygenometracks/tests/test_data/scale_bar.ini --region X:3200000-3300000 --trackLabelFraction 0.2 --width 38 --dpi 130 -o pygenometracks/tests/test_data/master_scale_bar_zoom.png
Expand Down
84 changes: 84 additions & 0 deletions pygenometracks/tests/test_arcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,72 @@
with open(os.path.join(ROOT, "short_long_arcs_incorrect2.ini"), 'w') as fh:
fh.write(browser_tracks.replace('compact_arcs_level = 2', 'compact_arcs_level = 2\nregion2=chrX:0-10'))

browser_tracks = """
[arcs]
title = default
file = short_long.arcs
color = bwr
height = 5
plot_arrows = true

[spacer]

[arcs]
file = short_long.arcs
color = bwr
height = 5
title = ylim = 6000000 (6Mb)
ylim = 6000000
plot_arrows = true

[spacer]

[arcs]
file = short_long.arcs
color = bwr
height = 5
title = ylim = 200000 (200kb)
ylim = 200000
plot_arrows = true

[spacer]

[arcs]
title = compact_arcs_level = 1
file = short_long.arcs
color = bwr
height = 5
compact_arcs_level = 1
plot_arrows = true

[spacer]

[arcs]
title = compact_arcs_level = 1 ylim = 6000000 (6Mb)
ylim = 6000000
file = short_long.arcs
color = bwr
height = 5
compact_arcs_level = 1
plot_arrows = true

[spacer]

[arcs]
title = compact_arcs_level = 2 line_style = dashed
file = short_long.arcs
color = bwr
height = 5
compact_arcs_level = 2
plot_arrows = true
line_style = dashed

[x-axis]
where = bottom
"""
with open(os.path.join(ROOT, "short_long_arcs_plot_arrows.ini"), 'w') as fh:
fh.write(browser_tracks)

browser_tracks = """
[x-axis]
where = top
Expand Down Expand Up @@ -382,6 +448,24 @@ def test_short_long_arcs():
os.remove(ini_file)


def test_use_plot_arrows():

outfile = NamedTemporaryFile(suffix='.png', prefix='pyGenomeTracks_test_',
delete=False)
ini_file = os.path.join(ROOT, "short_long_arcs_plot_arrows.ini")
region = "chr11:40000000-46000000"
expected_file = os.path.join(ROOT, 'master_short_long_arcs_plot_arrows.png')
args = f"--tracks {ini_file} --region {region} "\
"--trackLabelFraction 0.2 --width 38 --dpi 130 "\
f"--outFileName {outfile.name}".split()
pygenometracks.plotTracks.main(args)
res = compare_images(expected_file,
outfile.name, tolerance)
assert res is None, res

os.remove(outfile.name)


def test_use_middle_arcs():

outfile = NamedTemporaryFile(suffix='.png', prefix='pyGenomeTracks_test_',
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 62 additions & 0 deletions pygenometracks/tests/test_data/short_long_arcs_plot_arrows.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@

[arcs]
title = default
file = short_long.arcs
color = bwr
height = 5
plot_arrows = true

[spacer]

[arcs]
file = short_long.arcs
color = bwr
height = 5
title = ylim = 6000000 (6Mb)
ylim = 6000000
plot_arrows = true

[spacer]

[arcs]
file = short_long.arcs
color = bwr
height = 5
title = ylim = 200000 (200kb)
ylim = 200000
plot_arrows = true

[spacer]

[arcs]
title = compact_arcs_level = 1
file = short_long.arcs
color = bwr
height = 5
compact_arcs_level = 1
plot_arrows = true

[spacer]

[arcs]
title = compact_arcs_level = 1 ylim = 6000000 (6Mb)
ylim = 6000000
file = short_long.arcs
color = bwr
height = 5
compact_arcs_level = 1
plot_arrows = true

[spacer]

[arcs]
title = compact_arcs_level = 2 line_style = dashed
file = short_long.arcs
color = bwr
height = 5
compact_arcs_level = 2
plot_arrows = true
line_style = dashed

[x-axis]
where = bottom
70 changes: 57 additions & 13 deletions pygenometracks/tracks/LinksTrack.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
from intervaltree import IntervalTree, Interval
import matplotlib
import numpy as np
from matplotlib.patches import Arc, Polygon
import math
from matplotlib.patches import Arc, Polygon, FancyArrowPatch
from .. utilities import opener, to_string, change_chrom_names, temp_file_from_intersect, get_region
from tqdm import tqdm

Expand Down Expand Up @@ -66,6 +67,10 @@ class LinksTrack(GenomeTrack):
# The unit is bp. This corresponds to the longest arc you will see.
# This option is incompatible with compact_arcs_level = 2
#ylim = 100000
# You can add arrows on your arcs to indicate orientation
#plot_arrows = true
# You can adjust the size of arrowhead by playing with mutation_scale
#mutation_scale = 20
file_type = {TRACK_TYPE}
"""
DEFAULTS_PROPERTIES = {'links_type': 'arcs',
Expand All @@ -80,7 +85,9 @@ class LinksTrack(GenomeTrack):
'ylim': None,
'compact_arcs_level': '0',
'use_middle': False,
'region2': None}
'region2': None,
'plot_arrows': False,
'mutation_scale': 20}
NECESSARY_PROPERTIES = ['file']
SYNONYMOUS_PROPERTIES = {'max_value': {'auto': None},
'min_value': {'auto': None},
Expand All @@ -90,7 +97,7 @@ class LinksTrack(GenomeTrack):
'line_style': ['solid', 'dashed',
'dotted', 'dashdot'],
'compact_arcs_level': ['0', '1', '2']}
BOOLEAN_PROPERTIES = ['use_middle']
BOOLEAN_PROPERTIES = ['use_middle', 'plot_arrows']
STRING_PROPERTIES = ['file', 'file_type', 'overlay_previous',
'orientation', 'links_type', 'line_style',
'title', 'color', 'compact_arcs_level',
Expand All @@ -100,7 +107,8 @@ class LinksTrack(GenomeTrack):
'ylim': [0, np.inf],
'alpha': [0, 1],
'line_width': [0, np.inf],
'height': [0, np.inf]}
'height': [0, np.inf],
'mutation_scale': [0, np.inf]}
INTEGER_PROPERTIES = {}
# The color can be a color or a colormap (if there is a score)

Expand Down Expand Up @@ -202,7 +210,7 @@ def plot(self, ax, chrom_region, region_start, region_end):
for idx, interval in enumerate(arcs_in_region):
if self.properties['links_type'] == 'squares':
plotting_sides = {'as_in_data': False, 'mirrored': False}
start1, end1, start2, end2, _ = interval.data
start1, end1, start2, end2, _, _ = interval.data
if self.properties['region2'] is None:
temp_region2 = [chrom_region, region_start, region_end]
else:
Expand Down Expand Up @@ -298,10 +306,44 @@ def plot_arcs(self, ax, interval):
rgb = self.colormap.to_rgba(interval.data[4])
else:
rgb = self.properties['color']
ax.add_patch(Arc((center, 0), width,
2 * half_height, 0, 0, 180, color=rgb,
linewidth=self.current_line_width,
ls=self.properties['line_style']))
if not self.properties['plot_arrows']:
ax.add_patch(Arc((center, 0), width,
2 * half_height, 0, 0, 180, color=rgb,
linewidth=self.current_line_width,
ls=self.properties['line_style']))
else:
is_flipped = interval.data[5]
angle_in_rad = 0.01
rel_A_point = (width / 2 * math.cos(angle_in_rad),
half_height * math.sin(angle_in_rad))
angle_on_ellipse_in_rad = math.atan(rel_A_point[1] / rel_A_point[0])
if is_flipped:
ax.add_patch(Arc((center, 0), width,
2 * half_height, 0, 0,
180 - math.degrees(angle_on_ellipse_in_rad),
color=rgb,
linewidth=self.current_line_width,
ls=self.properties['line_style']))
ax.add_patch(FancyArrowPatch(posA=(center - rel_A_point[0], rel_A_point[1]),
posB=(interval.begin, 0),
arrowstyle='simple,tail_width=0',
mutation_scale=self.properties['mutation_scale'],
color=rgb, connectionstyle=f'arc3,rad={angle_in_rad * 0.2}',
linewidth=self.current_line_width / 2,
ls=self.properties['line_style']))
else:
ax.add_patch(Arc((center, 0), width,
2 * half_height, 0, math.degrees(angle_on_ellipse_in_rad),
180, color=rgb,
linewidth=self.current_line_width,
ls=self.properties['line_style']))
ax.add_patch(FancyArrowPatch(posA=(center + rel_A_point[0], rel_A_point[1]),
posB=(interval.end, 0),
arrowstyle='simple,tail_width=0',
mutation_scale=self.properties['mutation_scale'],
color=rgb, connectionstyle=f'arc3,rad=-{angle_in_rad * 0.2}',
linewidth=self.current_line_width / 2,
ls=self.properties['line_style']))

def plot_triangles(self, ax, interval):
x1 = interval.begin
Expand Down Expand Up @@ -499,23 +541,25 @@ def process_link_file(self, plot_regions):

if chrom1 not in interval_tree:
interval_tree[chrom1] = IntervalTree()
is_flipped = False
if start2 < start1 and not is_trans:
is_flipped = True
start1, start2 = start2, start1
end1, end2 = end2, end1
if self.properties['use_middle']:
mid1 = (start1 + end1) / 2
mid2 = (start2 + end2) / 2
if mid1 < mid2:
interval_tree[chrom1].add(Interval(mid1, mid2, [start1, end1, start2, end2, score]))
interval_tree[chrom1].add(Interval(mid1, mid2, [start1, end1, start2, end2, score, is_flipped]))
else:
interval_tree[chrom1].add(Interval(mid2, mid1, [start2, end2, start1, end1, score]))
interval_tree[chrom1].add(Interval(mid2, mid1, [start2, end2, start1, end1, score, is_flipped]))
else:
if not is_trans:
# each interval spans from the smallest start to the largest end
interval_tree[chrom1].add(Interval(start1, max(end1, end2), [start1, end1, start2, end2, score]))
interval_tree[chrom1].add(Interval(start1, max(end1, end2), [start1, end1, start2, end2, score, is_flipped]))
else:
# For the trans we keep start1 and end1
interval_tree[chrom1].add(Interval(start1, end1, [start1, end1, start2, end2, score]))
interval_tree[chrom1].add(Interval(start1, end1, [start1, end1, start2, end2, score, is_flipped]))
valid_intervals += 1

if valid_intervals == 0:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,6 @@ def checkProgramIsInstalled(self, program, args, where_to_download,
'Topic :: Scientific/Engineering :: Bio-Informatics'],
install_requires=install_requires_py,
zip_safe=False,
python_requires='>=3.7.*, <4',
python_requires='>=3.7,<4',
cmdclass={'sdist': sdist, 'install': install}
)