diff --git a/README.md b/README.md
index 571266d..1021649 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@
```bash
pip install tagore
tagore --version
-# tagore (version 1.0.1)
+# tagore (version 1.1.2)
```
### Requirements
@@ -60,13 +60,13 @@ Helper scripts for converting RFMix and ADMIXTURE outputs are included in the `s
A more complete example of a full chromosome painting using an RFMix output can be seen by running:
```bash
-./scripts/rfmix2tagore.py --chr1 example_ideogram/1KGP-MXL104_chr1.bed \
+rfmix2tagore --chr1 example_ideogram/1KGP-MXL104_chr1.bed \
--chr2 example_ideogram/1KGP-MXL104_chr2.bed \
--out example_ideogram/1KGP-MXL104_tagore.bed
tagore --input example_ideogram/1KGP-MXL104_tagore.bed \
--prefix example_ideogram/1KGP-MXL104 \
- --build hg37
+ --build hg37 \
--verbose
```
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..7fd26b9
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
\ No newline at end of file
diff --git a/scripts/rfmix2tagore.py b/scripts/rfmix2tagore.py
deleted file mode 100755
index 1a18a38..0000000
--- a/scripts/rfmix2tagore.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/bin/env python3
-__author__ = "Aroon Chande"
-__copyright__ = "Copyright 2019, Applied Bioinformatics Lab"
-__license__ = "MIT"
-
-import os
-import click
-
-commentBlock = '''
-# Each column is explained below:
-# 1. chr - The chromosome on which a feature has to be drawn
-# 2. start - Start position (in bp) for feature
-# 3. stop - Stop position (in bp) for feature
-# 4. feature - The shape of the feature to be drawn
-# 0 - will draw a rectangle
-# 1 - will draw a circle
-# 2 - will draw a triangle pointing to the genomic location
-# 3 - will draw a line at that genomic location
-# 5. size - The horizontal size of the feature. Should range between 0 and 1.
-# 6. color - Specify the color of the genomic feature with a hex value (#FF0000 for red, etc.)
-# 7. chrCopy - Specify the chromosome copy on which the feature should be drawn (1 or 2). To draw the same feature on both chromosomes, you must specify the feature twice
-'''
-
-
-@click.command()
-@click.option('--chr1', '-1', 'chr1', default=None, type=click.File('r'), help="Chromosome 1 RFMix painting")
-@click.option('--chr2', '-2', 'chr2', default=None, type=click.File('r'), help="Chromosome 2 RFMix painting")
-@click.option('--afr', 'afr', default="#0000ff ", help="Color for African blocks")
-@click.option('--eur', 'eur', default="#F4A500", help="Color for European blocks")
-@click.option('--nat', 'nat', default="#D92414", help="Color for Native American / Asian blocks")
-@click.option('--unk', 'unk', default="#808080 ", help="Color for Unknown regions")
-@click.option('--out', '-o', 'output', default=None, type=click.File('w'), help="Output da Vinci bed")
-def main(chr1, chr2, afr, eur, nat, unk, output):
- colors = {"UNK" : unk, "AFR" : afr, "IBS" : eur, "NAT" : nat}
- output.write("#chr\tstart\tstop\tfeature\tsize\tcolor\tchrCopy")
- output.write(commentBlock)
-# chr1 10000000 20000000 0 1 #FF0000 1
- for line in chr1:
- line = line.split('\t')
- bedLine = f"chr{line[0]}\t{line[1]}\t{line[2]}\t0\t1\t{colors[line[3]]}\t1"
- output.write(bedLine + "\n")
- for line in chr2:
- line = line.split('\t')
- bedLine = f"chr{line[0]}\t{line[1]}\t{line[2]}\t0\t1\t{colors[line[3]]}\t2"
- output.write(bedLine + "\n")
-
-
-if __name__ == "__main__":
- main()
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..791c947
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,43 @@
+[metadata]
+name = tagore
+version = 1.1.2
+author = Applied Bioinformatics Laboratory
+author_email = abil@ihrc.com
+description = A simple way to visualize features on human chromosome ideograms
+keywords =
+ Human
+ chromosome
+ ideogram
+ visualization
+url = https://github.com/jordanlab/tagore
+long_description = file: README.md
+long_description_content_type = text/markdown
+classifiers =
+ Programming Language :: Python :: 3
+ License :: OSI Approved :: GNU General Public License v3 (GPLv3)
+ Operating System :: MacOS
+ Operating System :: POSIX
+ Environment :: Console
+ Development Status :: 5 - Production/Stable
+ Topic :: Scientific/Engineering :: Visualization
+
+[options]
+name = tagore
+packages = find_namespace:
+include_package_data = True
+install_requires = click
+python_requires = >=3.6
+# scripts = tagore/scripts/rfmix2tagore.py
+
+[options.packages.find]
+where=src
+exclude=tagore.tests*
+
+[options.entry_points]
+console_scripts =
+ tagore = tagore.main:run
+ rfmix2tagore = tagore.scripts.rfmix2tagore:main
+
+
+[options.package_data]
+* = *.svg.p
\ No newline at end of file
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 023b443..0000000
--- a/setup.py
+++ /dev/null
@@ -1,30 +0,0 @@
-import setuptools
-
-with open("README.md", "r") as fh:
- long_description = fh.read()
-
-setuptools.setup(
- name="tagore",
- version="1.1.1",
- author="Applied Bioinformatics Laboratory",
- author_email="abil@ihrc.com",
- description="A simple way to visualize features on human chromosome ideograms",
- long_description=long_description,
- long_description_content_type="text/markdown",
- url="https://github.com/jordanlab/tagore",
- packages=setuptools.find_packages(),
- classifiers=[
- "Programming Language :: Python :: 3",
- "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
- "Operating System :: MacOS",
- "Operating System :: POSIX",
- "Environment :: Console",
- "Development Status :: 5 - Production/Stable",
- "Topic :: Scientific/Engineering :: Visualization"
- ],
- python_requires='>=3.6',
- install_requires=['click'],
- keywords = ['Human', 'chromosome', 'ideogram', 'visualization'],
- scripts = ['tagore', 'scripts/rfmix2tagore.py'],
- data_files = [('lib/tagore-data/', ['base.svg.p'])]
-)
\ No newline at end of file
diff --git a/src/tagore/__init__.py b/src/tagore/__init__.py
new file mode 100644
index 0000000..0f802cf
--- /dev/null
+++ b/src/tagore/__init__.py
@@ -0,0 +1 @@
+# from . import run
diff --git a/base.svg.p b/src/tagore/base.svg.p
similarity index 100%
rename from base.svg.p
rename to src/tagore/base.svg.p
diff --git a/src/tagore/main.py b/src/tagore/main.py
new file mode 100755
index 0000000..0aa5847
--- /dev/null
+++ b/src/tagore/main.py
@@ -0,0 +1,395 @@
+#!/usr/bin/env python3
+"""
+tagore: a utility for illustrating human chromosomes
+https://github.com/jordanlab/tagore
+"""
+__author__ = ["Lavanya Rishishar", "Aroon Chande"]
+__copyright__ = "Copyright 2019, Applied Bioinformatics Lab"
+__license__ = "GPLv3"
+
+import os
+import pickle
+import pkgutil
+import re
+import shutil
+import subprocess
+import sys
+from argparse import ArgumentParser, HelpFormatter
+
+VERSION = "1.1.2"
+
+COORDINATES = {
+ "1": {"cx": 128.6, "cy": 1.5, "ht": 1654.5, "width": 118.6},
+ "2": {"cx": 301.4, "cy": 43.6, "ht": 1612.4, "width": 118.6},
+ "3": {"cx": 477.6, "cy": 341.4, "ht": 1314.7, "width": 118.6},
+ "4": {"cx": 655.6, "cy": 517.9, "ht": 1138.1, "width": 118.6},
+ "5": {"cx": 835.4, "cy": 461, "ht": 1195.1, "width": 118.6},
+ "6": {"cx": 1012.4, "cy": 524.2, "ht": 1131.8, "width": 118.6},
+ "7": {"cx": 1198.2, "cy": 608.5, "ht": 1047.5, "width": 118.6},
+ "8": {"cx": 1372.9, "cy": 692.8, "ht": 963.2, "width": 118.6},
+ "9": {"cx": 1554.5, "cy": 724.4, "ht": 931.6, "width": 118.6},
+ "10": {"cx": 1733.8, "cy": 766.6, "ht": 889.4, "width": 118.6},
+ "11": {"cx": 1911.5, "cy": 766.6, "ht": 889.4, "width": 118.6},
+ "12": {"cx": 2095.6, "cy": 769.7, "ht": 886.3, "width": 118.6},
+ "13": {"cx": 129.3, "cy": 2068.8, "ht": 766.1, "width": 118.6},
+ "14": {"cx": 301.6, "cy": 2121.5, "ht": 713.4, "width": 118.6},
+ "15": {"cx": 477.5, "cy": 2153.1, "ht": 681.8, "width": 118.6},
+ "16": {"cx": 656.7, "cy": 2232.2, "ht": 602.8, "width": 118.6},
+ "17": {"cx": 841.2, "cy": 2290.7, "ht": 544.3, "width": 118.6},
+ "18": {"cx": 1015.7, "cy": 2313.9, "ht": 521.1, "width": 118.6},
+ "19": {"cx": 1199.5, "cy": 2437.2, "ht": 397.8, "width": 118.6},
+ "20": {"cx": 1374.4, "cy": 2416.1, "ht": 418.9, "width": 118.6},
+ "21": {"cx": 1553, "cy": 2510.9, "ht": 324.1, "width": 118.6},
+ "22": {"cx": 1736.9, "cy": 2489.8, "ht": 345.1, "width": 118.6},
+ "X": {"cx": 1915.7, "cy": 1799.21, "ht": 1035.4, "width": 118.6},
+ "Y": {"cx": 2120.9, "cy": 2451.6, "ht": 382.7, "width": 118.6},
+}
+
+CHROM_SIZES = {
+ "hg37": {
+ "1": 249250621,
+ "2": 243199373,
+ "3": 198022430,
+ "4": 191154276,
+ "5": 180915260,
+ "6": 171115067,
+ "7": 159138663,
+ "8": 146364022,
+ "9": 141213431,
+ "10": 135534747,
+ "11": 135006516,
+ "12": 133851895,
+ "13": 115169878,
+ "14": 107349540,
+ "15": 102531392,
+ "16": 90354753,
+ "17": 81195210,
+ "18": 78077248,
+ "19": 59128983,
+ "20": 63025520,
+ "21": 48129895,
+ "22": 51304566,
+ "X": 155270560,
+ "Y": 59373566,
+ },
+ "hg38": {
+ "1": 248956422,
+ "2": 242193529,
+ "3": 198295559,
+ "4": 190214555,
+ "5": 181538259,
+ "6": 170805979,
+ "7": 159345973,
+ "8": 145138636,
+ "9": 138394717,
+ "10": 133797422,
+ "11": 135086622,
+ "12": 133275309,
+ "13": 114364328,
+ "14": 107043718,
+ "15": 101991189,
+ "16": 90338345,
+ "17": 83257441,
+ "18": 80373285,
+ "19": 58617616,
+ "20": 64444167,
+ "21": 46709983,
+ "22": 50818468,
+ "X": 156040895,
+ "Y": 57227415,
+ },
+}
+
+
+def printif(statement, condition):
+ """
+ Print statements if a boolean (e.g. verbose) is true
+ """
+ if condition:
+ print(statement)
+
+
+def draw(arguments, svg_header, svg_footer):
+ """
+ Create the SVG object
+ """
+ polygons = ""
+ try:
+ input_fh = open(arguments.input, "r")
+ except (IOError, EOFError) as input_fh_e:
+ print("Error opening input file!")
+ raise input_fh_e
+ svg_fn = f"{arguments.prefix}.svg"
+ try:
+ svg_fh = open(svg_fn, "w")
+ svg_fh.write(svg_header)
+ except (IOError, EOFError) as svg_fh_e:
+ print("Error opening output file!")
+ raise svg_fh_e
+ line_num = 1
+ for entry in input_fh:
+ if entry.startswith("#"):
+ continue
+ entry = entry.rstrip().split("\t")
+ if len(entry) != 7:
+ print(f"Line number {line_num} does not have 7 columns")
+ sys.exit()
+ chrm, start, stop, feature, size, col, chrcopy = entry
+ chrm = chrm.replace("chr", "")
+ start = int(start)
+ stop = int(stop)
+ size = float(size)
+ feature = int(feature)
+ chrcopy = int(chrcopy)
+ if 0 > size > 1:
+ print(
+ f"Feature size, {size},on line {line_num} unclear. \
+ Please bound the size between 0 (0%) to 1 (100%). Defaulting to 1."
+ )
+ size = 1
+ if not re.match("^#.{6}", col):
+ print(
+ f"Feature color, {col}, on line {line_num} unclear. \
+ Please define the color in hex starting with #. Defaulting to #000000."
+ )
+ col = "#000000"
+ if chrcopy not in [1, 2]:
+ print(
+ f"Feature chromosome copy, {chrcopy}, on line {line_num}\
+ unclear. Skipping..."
+ )
+ line_num = line_num + 1
+ continue
+ line_num = line_num + 1
+ if feature == 0: # Rectangle
+ feat_start = (
+ start * COORDINATES[chrm]["ht"] / CHROM_SIZES[arguments.build][chrm]
+ )
+ feat_end = (
+ stop * COORDINATES[chrm]["ht"] / CHROM_SIZES[arguments.build][chrm]
+ )
+ width = COORDINATES[chrm]["width"] * size / 2
+ if chrcopy == 1:
+ x_pos = COORDINATES[chrm]["cx"] - width
+ else:
+ x_pos = COORDINATES[chrm]["cx"]
+ y_pos = COORDINATES[chrm]["cy"] + feat_start
+ height = feat_end - feat_start
+ svg_fh.write(
+ f''
+ + "\n"
+ )
+ elif feature == 1: # Circle
+ feat_start = (
+ start * COORDINATES[chrm]["ht"] / CHROM_SIZES[arguments.build][chrm]
+ )
+ feat_end = (
+ stop * COORDINATES[chrm]["ht"] / CHROM_SIZES[arguments.build][chrm]
+ )
+ radius = COORDINATES[chrm]["width"] * size / 4
+ if chrcopy == 1:
+ x_pos = COORDINATES[chrm]["cx"] - COORDINATES[chrm]["width"] / 4
+ else:
+ x_pos = COORDINATES[chrm]["cx"] + COORDINATES[chrm]["width"] / 4
+ y_pos = COORDINATES[chrm]["cy"] + (feat_start + feat_end) / 2
+ svg_fh.write(
+ f''
+ + "\n"
+ )
+ elif feature == 2: # Triangle
+ feat_start = (
+ start * COORDINATES[chrm]["ht"] / CHROM_SIZES[arguments.build][chrm]
+ )
+ feat_end = (
+ stop * COORDINATES[chrm]["ht"] / CHROM_SIZES[arguments.build][chrm]
+ )
+ if chrcopy == 1:
+ x_pos = COORDINATES[chrm]["cx"] - COORDINATES[chrm]["width"] / 2
+ sx_pos = 38.2 * size
+ else:
+ x_pos = COORDINATES[chrm]["cx"] + COORDINATES[chrm]["width"] / 2
+ sx_pos = -38.2 * size
+ y_pos = COORDINATES[chrm]["cy"] + (feat_start + feat_end) / 2
+ sy_pos = 21.5 * size
+ polygons += (
+ f''
+ + "\n"
+ )
+ elif feature == 3: # Line
+ y_pos1 = (
+ start * COORDINATES[chrm]["ht"] / CHROM_SIZES[arguments.build][chrm]
+ )
+ y_pos2 = stop * COORDINATES[chrm]["ht"] / CHROM_SIZES[arguments.build][chrm]
+ y_pos = (y_pos1 + y_pos2) / 2
+ y_pos += COORDINATES[chrm]["cy"]
+ if chrcopy == 1:
+ x_pos1 = COORDINATES[chrm]["cx"] - COORDINATES[chrm]["width"] / 2
+ x_pos2 = COORDINATES[chrm]["cx"]
+ svg_fh.write(
+ f''
+ + "\n"
+ )
+ else:
+ x_pos1 = COORDINATES[chrm]["cx"]
+ x_pos2 = COORDINATES[chrm]["cx"] + COORDINATES[chrm]["width"] / 2
+ svg_fh.write(
+ f''
+ + "\n"
+ )
+ else:
+ print(
+ f"Feature type, {feature}, unclear. Please use either 0, 1, 2 or 3. Skipping..."
+ )
+ continue
+ svg_fh.write(svg_footer)
+ svg_fh.write(polygons)
+ svg_fh.write("")
+ svg_fh.close()
+ printif(f"\033[92mSuccessfully created SVG\033[0m", arguments.verbose)
+
+
+def run():
+ parser = ArgumentParser(
+ prog="tagore",
+ add_help=True,
+ description="""
+ tagore: a utility for illustrating human chromosomes
+ https://github.com/jordanlab/tagore
+ """,
+ formatter_class=lambda prog: HelpFormatter(
+ prog, width=120, max_help_position=120
+ ),
+ )
+
+ parser.add_argument(
+ "--version",
+ action="version",
+ help="Print the software version",
+ version="tagore (version {})".format(VERSION),
+ )
+
+ # Input arguments
+ parser.add_argument(
+ "-i",
+ "--input",
+ required=True,
+ default=None,
+ metavar="",
+ help="Input BED-like file",
+ )
+ parser.add_argument(
+ "-p",
+ "--prefix",
+ required=False,
+ default="out",
+ metavar="[output file prefix]",
+ help='Output prefix [Default: "out"]',
+ )
+ parser.add_argument(
+ "-b",
+ "--build",
+ required=False,
+ default="hg38",
+ metavar="[hg78/hg38]",
+ help="Human genome build to use [Default: hg38]",
+ )
+ parser.add_argument(
+ "-f",
+ "--force",
+ required=False,
+ default=False,
+ help="Overwrite output files if they exist already",
+ action="store_true",
+ )
+ parser.add_argument(
+ "-ofmt",
+ "--oformat",
+ required=False,
+ default="png",
+ help="Output format for conversion (pdf requires rsvg-convert)",
+ metavar="[png/pdf]",
+ )
+ parser.add_argument(
+ "-v",
+ "--verbose",
+ required=False,
+ default=False,
+ help="Display verbose output",
+ action="store_true",
+ )
+ parsed_args, unknown_args = parser.parse_known_args()
+ if unknown_args:
+ print(
+ f"\033[93mOne or more unknown arguments were supplied:\033[0m {' '.join(unknown_args)}\n"
+ )
+ parser.print_help()
+ sys.exit()
+ if parsed_args.build not in ["hg37", "hg38"]:
+ print(
+ f"\033[91mBuild must be either 'hg37' or 'hg38', you supplied {parsed_args.build}.\033[0m"
+ )
+ sys.exit()
+
+ if shutil.which("rsvg-convert", mode=os.X_OK) is None:
+ if shutil.which("rsvg", mode=os.X_OK) is None:
+ print(f"\033[91mCould not find `rsvg` or `rsvg-convert` in PATH.\033[0m")
+ sys.exit()
+ else:
+ is_rsvg_installed = True
+ if parsed_args.oformat != "png":
+ print(f"\033[93m`rsvg` only supports PNG output, using png\033[0m")
+ parsed_args.oformat = "png"
+ else:
+ is_rsvg_installed = False
+ if parsed_args.oformat not in ["png", "pdf"]:
+ print(f"\033[93m{parsed_args.oformat} is not PNG or PDF, using PNG\033[0m")
+ parsed_args.oformat = "png"
+ svg_pkl_data = pkgutil.get_data("tagore", "base.svg.p")
+ svg_header, svg_footer = pickle.loads(svg_pkl_data)
+ printif(
+ f"\033[94mDrawing chromosome ideogram using {parsed_args.input}\033[0m",
+ parsed_args.verbose,
+ )
+ if os.path.exists(f"{parsed_args.prefix}.svg") and parsed_args.force is False:
+ print(f"\033[93m'{parsed_args.prefix}.svg' already exists.\033[0m")
+ OW = input(f"Overwrite {parsed_args.prefix}.svg? [Y/n]: ") or "y"
+ if OW.lower() != "y":
+ print(f"\033[93m'tagore will now exit...\033[0m")
+ sys.exit()
+ else:
+ print(
+ f"\033[94mOverwriting existing file and saving to: {parsed_args.prefix}.svg\033[0m"
+ )
+ else:
+ printif(
+ f"\033[94mSaving to: {parsed_args.prefix}.svg\033[0m", parsed_args.verbose
+ )
+ draw(parsed_args, svg_header, svg_footer)
+ printif(
+ f"\033[94mConverting {parsed_args.prefix}.svg -> {parsed_args.prefix}.{parsed_args.oformat} \033[0m",
+ parsed_args.verbose,
+ )
+ try:
+ if is_rsvg_installed:
+ subprocess.check_output(
+ f"rsvg {parsed_args.prefix}.svg {parsed_args.prefix}.{parsed_args.oformat} ",
+ shell=True,
+ )
+ else:
+ subprocess.check_output(
+ f"rsvg-convert -o {parsed_args.prefix}.{parsed_args.oformat} -f {parsed_args.oformat} {parsed_args.prefix}.svg ",
+ shell=True,
+ )
+ except subprocess.CalledProcessError as rsvg_e:
+ printif(f"\033[91mFailed SVG to PNG conversion...\033[0m", parsed_args.verbose)
+ raise rsvg_e
+ finally:
+ printif(
+ f"\033[92mSuccessfully converted SVG to {parsed_args.oformat.upper()}\033[0m",
+ parsed_args.verbose,
+ )
diff --git a/scripts/LICENSE b/src/tagore/scripts/LICENSE
similarity index 100%
rename from scripts/LICENSE
rename to src/tagore/scripts/LICENSE
diff --git a/src/tagore/scripts/rfmix2tagore.py b/src/tagore/scripts/rfmix2tagore.py
new file mode 100755
index 0000000..ec92123
--- /dev/null
+++ b/src/tagore/scripts/rfmix2tagore.py
@@ -0,0 +1,73 @@
+#!/bin/env python3
+__author__ = "Aroon Chande"
+__copyright__ = "Copyright 2019, Applied Bioinformatics Lab"
+__license__ = "MIT"
+
+import os
+
+import click
+
+commentBlock = """
+# Each column is explained below:
+# 1. chr - The chromosome on which a feature has to be drawn
+# 2. start - Start position (in bp) for feature
+# 3. stop - Stop position (in bp) for feature
+# 4. feature - The shape of the feature to be drawn
+# 0 - will draw a rectangle
+# 1 - will draw a circle
+# 2 - will draw a triangle pointing to the genomic location
+# 3 - will draw a line at that genomic location
+# 5. size - The horizontal size of the feature. Should range between 0 and 1.
+# 6. color - Specify the color of the genomic feature with a hex value (#FF0000 for red, etc.)
+# 7. chrCopy - Specify the chromosome copy on which the feature should be drawn (1 or 2). To draw the same feature on both chromosomes, you must specify the feature twice
+"""
+
+
+@click.command()
+@click.option(
+ "--chr1",
+ "-1",
+ "chr1",
+ default=None,
+ type=click.File("r"),
+ help="Chromosome 1 RFMix painting",
+)
+@click.option(
+ "--chr2",
+ "-2",
+ "chr2",
+ default=None,
+ type=click.File("r"),
+ help="Chromosome 2 RFMix painting",
+)
+@click.option("--afr", "afr", default="#0000ff ", help="Color for African blocks")
+@click.option("--eur", "eur", default="#F4A500", help="Color for European blocks")
+@click.option(
+ "--nat", "nat", default="#D92414", help="Color for Native American / Asian blocks"
+)
+@click.option("--unk", "unk", default="#808080 ", help="Color for Unknown regions")
+@click.option(
+ "--out",
+ "-o",
+ "output",
+ default=None,
+ type=click.File("w"),
+ help="Output da Vinci bed",
+)
+def main(chr1, chr2, afr, eur, nat, unk, output):
+ colors = {"UNK": unk, "AFR": afr, "IBS": eur, "NAT": nat}
+ output.write("#chr\tstart\tstop\tfeature\tsize\tcolor\tchrCopy")
+ output.write(commentBlock)
+ # chr1 10000000 20000000 0 1 #FF0000 1
+ for line in chr1:
+ line = line.split("\t")
+ bedLine = f"chr{line[0]}\t{line[1]}\t{line[2]}\t0\t1\t{colors[line[3]]}\t1"
+ output.write(bedLine + "\n")
+ for line in chr2:
+ line = line.split("\t")
+ bedLine = f"chr{line[0]}\t{line[1]}\t{line[2]}\t0\t1\t{colors[line[3]]}\t2"
+ output.write(bedLine + "\n")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/00_printif_test.py b/src/tagore/tests/00_printif_test.py
similarity index 73%
rename from tests/00_printif_test.py
rename to src/tagore/tests/00_printif_test.py
index d14a062..2b835d8 100644
--- a/tests/00_printif_test.py
+++ b/src/tagore/tests/00_printif_test.py
@@ -1,20 +1,26 @@
#!/bin/env python3
-import pytest
import sys
-from os import getcwd, path, pardir
+from os import getcwd, pardir, path
+
+import pytest
+
sys.path.append(path.abspath(path.join(getcwd())))
from tagore import printif
+
def test_printif_true():
from io import StringIO
+
output = StringIO()
sys.stdout = output
printif("Test passed", True)
- assert output.getvalue().strip() == "Test passed"
+ assert output.getvalue().strip() == "Test passed"
+
def test_printif_false():
from io import StringIO
+
output = StringIO()
sys.stdout = output
printif("Test false", False)
- assert output.getvalue().strip() == ''
\ No newline at end of file
+ assert output.getvalue().strip() == ""
diff --git a/tagore b/tagore
deleted file mode 100755
index bace594..0000000
--- a/tagore
+++ /dev/null
@@ -1,273 +0,0 @@
-#!/usr/bin/env python3
-'''
-tagore: a utility for illustrating human chromosomes
-https://github.com/jordanlab/tagore
-'''
-__author__ = ["Lavanya Rishishar", "Aroon Chande"]
-__copyright__ = "Copyright 2019, Applied Bioinformatics Lab"
-__license__ = "GPLv3"
-
-import os
-import pickle
-import re
-import shutil
-import subprocess
-import sys
-from argparse import ArgumentParser, HelpFormatter
-
-
-VERSION = '1.1.1'
-
-COORDINATES = {
- "1": {"cx": 128.6, "cy": 1.5, "ht": 1654.5, "width": 118.6},
- "2": {"cx": 301.4, "cy": 43.6, "ht": 1612.4, "width": 118.6},
- "3": {"cx": 477.6, "cy": 341.4, "ht": 1314.7, "width": 118.6},
- "4": {"cx": 655.6, "cy": 517.9, "ht": 1138.1, "width": 118.6},
- "5": {"cx": 835.4, "cy": 461, "ht": 1195.1, "width": 118.6},
- "6": {"cx": 1012.4, "cy": 524.2, "ht": 1131.8, "width": 118.6},
- "7": {"cx": 1198.2, "cy": 608.5, "ht": 1047.5, "width": 118.6},
- "8": {"cx": 1372.9, "cy": 692.8, "ht": 963.2, "width": 118.6},
- "9": {"cx": 1554.5, "cy": 724.4, "ht": 931.6, "width": 118.6},
- "10": {"cx": 1733.8, "cy": 766.6, "ht": 889.4, "width": 118.6},
- "11": {"cx": 1911.5, "cy": 766.6, "ht": 889.4, "width": 118.6},
- "12": {"cx": 2095.6, "cy": 769.7, "ht": 886.3, "width": 118.6},
- "13": {"cx": 129.3, "cy": 2068.8, "ht": 766.1, "width": 118.6},
- "14": {"cx": 301.6, "cy": 2121.5, "ht": 713.4, "width": 118.6},
- "15": {"cx": 477.5, "cy": 2153.1, "ht": 681.8, "width": 118.6},
- "16": {"cx": 656.7, "cy": 2232.2, "ht": 602.8, "width": 118.6},
- "17": {"cx": 841.2, "cy": 2290.7, "ht": 544.3, "width": 118.6},
- "18": {"cx": 1015.7, "cy": 2313.9, "ht": 521.1, "width": 118.6},
- "19": {"cx": 1199.5, "cy": 2437.2, "ht": 397.8, "width": 118.6},
- "20": {"cx": 1374.4, "cy": 2416.1, "ht": 418.9, "width": 118.6},
- "21": {"cx": 1553, "cy": 2510.9, "ht": 324.1, "width": 118.6},
- "22": {"cx": 1736.9, "cy": 2489.8, "ht": 345.1, "width": 118.6},
- "X": {"cx": 1915.7, "cy": 1799.21, "ht": 1035.4, "width": 118.6},
- "Y": {"cx": 2120.9, "cy": 2451.6, "ht": 382.7, "width": 118.6},
-
-}
-
-CHROM_SIZES = {
- "hg37": {
- "1": 249250621, "2": 243199373, "3": 198022430, "4": 191154276,
- "5": 180915260, "6": 171115067, "7": 159138663, "8": 146364022,
- "9": 141213431, "10": 135534747, "11": 135006516, "12": 133851895,
- "13": 115169878, "14": 107349540, "15": 102531392, "16": 90354753,
- "17": 81195210, "18": 78077248, "19": 59128983, "20": 63025520,
- "21": 48129895, "22": 51304566, "X": 155270560, "Y": 59373566
- },
- "hg38": {
- "1": 248956422, "2": 242193529, "3": 198295559, "4": 190214555,
- "5": 181538259, "6": 170805979, "7": 159345973, "8": 145138636,
- "9": 138394717, "10": 133797422, "11": 135086622, "12": 133275309,
- "13": 114364328, "14": 107043718, "15": 101991189, "16": 90338345,
- "17": 83257441, "18": 80373285, "19": 58617616, "20": 64444167,
- "21": 46709983, "22": 50818468, "X": 156040895, "Y": 57227415
- },
-}
-
-def printif(statement, condition):
- '''
- Print statements if a boolean (e.g. verbose) is true
- '''
- if condition:
- print(statement)
-
-def draw(arguments):
- '''
- Create the SVG object
- '''
- polygons = ""
- try:
- input_fh = open(arguments.input, 'r')
- except (IOError, EOFError) as input_fh_e:
- print("Error opening input file!")
- raise input_fh_e
- svg_fn = f"{arguments.prefix}.svg"
- try:
- svg_fh = open(svg_fn, 'w')
- svg_fh.write(__head__)
- except (IOError, EOFError) as svg_fh_e:
- print("Error opening output file!")
- raise svg_fh_e
- line_num = 1
- for entry in input_fh:
- if entry.startswith("#"):
- continue
- entry = entry.rstrip().split("\t")
- if len(entry) != 7:
- print(f"Line number {line_num} does not have 7 columns")
- sys.exit()
- chrm, start, stop, feature, size, col, chrcopy = entry
- chrm = chrm.replace('chr', '')
- start = int(start)
- stop = int(stop)
- size = float(size)
- feature = int(feature)
- chrcopy = int(chrcopy)
- if 0 > size > 1:
- print(f"Feature size, {size},on line {line_num} unclear. \
- Please bound the size between 0 (0%) to 1 (100%). Defaulting to 1.")
- size = 1
- if not re.match("^#.{6}", col):
- print(f"Feature color, {col}, on line {line_num} unclear. \
- Please define the color in hex starting with #. Defaulting to #000000.")
- col = "#000000"
- if chrcopy not in [1, 2]:
- print(f"Feature chromosome copy, {chrcopy}, on line {line_num}\
- unclear. Skipping...")
- line_num = line_num + 1
- continue
- line_num = line_num + 1
- if feature == 0: # Rectangle
- feat_start = start*COORDINATES[chrm]["ht"]/CHROM_SIZES[arguments.build][chrm]
- feat_end = stop*COORDINATES[chrm]["ht"]/CHROM_SIZES[arguments.build][chrm]
- width = COORDINATES[chrm]["width"]*size/2
- if chrcopy == 1:
- x_pos = COORDINATES[chrm]["cx"] - width
- else:
- x_pos = COORDINATES[chrm]["cx"]
- y_pos = COORDINATES[chrm]["cy"] + feat_start
- height = feat_end-feat_start
- svg_fh.write(f"" + "\n")
- elif feature == 1: # Circle
- feat_start = start*COORDINATES[chrm]["ht"]/CHROM_SIZES[arguments.build][chrm]
- feat_end = stop*COORDINATES[chrm]["ht"]/CHROM_SIZES[arguments.build][chrm]
- radius = COORDINATES[chrm]["width"]*size/4
- if chrcopy == 1:
- x_pos = COORDINATES[chrm]["cx"] - COORDINATES[chrm]["width"]/4
- else:
- x_pos = COORDINATES[chrm]["cx"] + COORDINATES[chrm]["width"]/4
- y_pos = COORDINATES[chrm]["cy"]+(feat_start+feat_end)/2
- svg_fh.write(f"" + "\n")
- elif feature == 2: # Triangle
- feat_start = start*COORDINATES[chrm]["ht"]/CHROM_SIZES[arguments.build][chrm]
- feat_end = stop*COORDINATES[chrm]["ht"]/CHROM_SIZES[arguments.build][chrm]
- if chrcopy == 1:
- x_pos = COORDINATES[chrm]["cx"] - COORDINATES[chrm]["width"]/2
- sx_pos = 38.2*size
- else:
- x_pos = COORDINATES[chrm]["cx"] + COORDINATES[chrm]["width"]/2
- sx_pos = -38.2*size
- y_pos = COORDINATES[chrm]["cy"]+(feat_start+feat_end)/2
- sy_pos = 21.5*size
- polygons += f"" + "\n"
- elif feature == 3: # Line
- y_pos1 = start*COORDINATES[chrm]["ht"]/CHROM_SIZES[arguments.build][chrm]
- y_pos2 = stop*COORDINATES[chrm]["ht"]/CHROM_SIZES[arguments.build][chrm]
- y_pos = (y_pos1+y_pos2)/2
- y_pos += COORDINATES[chrm]["cy"]
- if chrcopy == 1:
- x_pos1 = COORDINATES[chrm]["cx"] - COORDINATES[chrm]["width"]/2
- x_pos2 = COORDINATES[chrm]["cx"]
- svg_fh.write(f"" + "\n")
- else:
- x_pos1 = COORDINATES[chrm]["cx"]
- x_pos2 = COORDINATES[chrm]["cx"] + COORDINATES[chrm]["width"]/2
- svg_fh.write(f"" + "\n")
- else:
- print(f"Feature type, {feature}, unclear. Please use either 0, 1, 2 or 3. Skipping...")
- continue
- svg_fh.write(__tail__)
- svg_fh.write(polygons)
- svg_fh.write("")
- svg_fh.close()
- printif(f"\033[92mSuccessfully created SVG\033[0m", ARGS.verbose)
-
-
-if __name__ == "__main__":
- PARSER = ArgumentParser(prog="tagore",
- add_help=True,
- description='''
- tagore: a utility for illustrating human chromosomes
- https://github.com/jordanlab/tagore
- ''',
- formatter_class=lambda prog: HelpFormatter(prog, width=120,
- max_help_position=120))
-
- PARSER.add_argument('--version', action='version',
- help='Print the software version',
- version='tagore (version {})'.format(VERSION))
-
- # Input arguments
- PARSER.add_argument('-i', '--input', required=True, default=None, metavar='',
- help='Input BED-like file')
- PARSER.add_argument('-p', '--prefix', required=False, default="out",
- metavar='[output file prefix]',
- help='Output prefix [Default: "out"]')
- PARSER.add_argument('-b', '--build', required=False, default="hg38",
- metavar='[hg78/hg38]',
- help="Human genome build to use [Default: hg38]")
- PARSER.add_argument('-f', '--force', required=False, default=False,
- help="Overwrite output files if they exist already",
- action="store_true")
- PARSER.add_argument('-ofmt', '--oformat', required=False, default='png',
- help="Output format for conversion (pdf requires rsvg-convert)",
- metavar='[png/pdf]')
- PARSER.add_argument('-v', '--verbose', required=False, default=False,
- help="Display verbose output",
- action="store_true")
- ARGS, UNKARGS = PARSER.parse_known_args()
- if UNKARGS:
- print(f"\033[93mOne or more unknown arguments were supplied:\033[0m {' '.join(UNKARGS)}\n")
- PARSER.print_help()
- sys.exit()
- if ARGS.build not in ['hg37', 'hg38']:
- print(f"\033[91mBuild must be either 'hg37' or 'hg38', you supplied {ARGS.build}.\033[0m")
- sys.exit()
-
- if shutil.which("rsvg-convert", mode=os.X_OK) is None:
- if shutil.which("rsvg", mode=os.X_OK) is None:
- print(f"\033[91mCould not find `rsvg` or `rsvg-convert` in PATH.\033[0m")
- sys.exit()
- else:
- RSVG = True
- if ARGS.oformat != "png":
- print(f"\033[93m`rsvg` only supports PNG output, using png\033[0m")
- ARGS.oformat = "png"
- else:
- RSVG = False
- if ARGS.oformat not in ["png", "pdf"]:
- print(f"\033[93m{ARGS.oformat} is not PNG or PDF, using PNG\033[0m")
- ARGS.oformat = "png"
- BASE_PATH = os.path.join(sys.prefix, 'lib', 'tagore-data', 'base.svg.p')
- try:
- BASE = open(BASE_PATH, 'rb')
- except (IOError, EOFError) as base_e:
- BASE_PATH = os.path.join(site.USER_BASE, 'lib', 'tagore-data', 'base.svg.p')
- try:
- BASE = open(BASE_PATH, 'rb')
- except (IOError, EOFError) as base_e:
- print(f"\033[91mCould not open {BASE_PATH}. Please reinstall tagore\033[0m")
- raise base_e
- __head__, __tail__ = pickle.load(BASE)
- printif(f"\033[94mDrawing chromosome ideogram using {ARGS.input}\033[0m", ARGS.verbose)
- if os.path.exists(f"{ARGS.prefix}.svg") and ARGS.force is False:
- print(f"\033[93m'{ARGS.prefix}.svg' already exists.\033[0m")
- OW = input(f"Overwrite {ARGS.prefix}.svg? [Y/n]: ") or "y"
- if OW.lower() != "y":
- print(f"\033[93m'tagore will now exit...\033[0m")
- sys.exit()
- else:
- print(f"\033[94mOverwriting existing file and saving to: {ARGS.prefix}.svg\033[0m")
- else:
- printif(f"\033[94mSaving to: {ARGS.prefix}.svg\033[0m", ARGS.verbose)
- draw(ARGS)
- printif(f"\033[94mConverting {ARGS.prefix}.svg -> {ARGS.prefix}.{ARGS.oformat} \033[0m",
- ARGS.verbose)
- try:
- if RSVG:
- subprocess.check_output(f"rsvg {ARGS.prefix}.svg {ARGS.prefix}.{ARGS.oformat} ",
- shell=True)
- else:
- subprocess.check_output(f"rsvg-convert -o {ARGS.prefix}.{ARGS.oformat} -f {ARGS.oformat} {ARGS.prefix}.svg ", shell=True)
- except subprocess.CalledProcessError as rsvg_e:
- printif(f"\033[91mFailed SVG to PNG conversion...\033[0m", ARGS.verbose)
- raise rsvg_e
- finally:
- printif(f"\033[92mSuccessfully converted SVG to {ARGS.oformat.upper()}\033[0m",
- ARGS.verbose)