Skip to content

Commit

Permalink
Compress reads table and encode it directly into HTML page
Browse files Browse the repository at this point in the history
  • Loading branch information
kvg committed Apr 27, 2024
1 parent c7ab6d7 commit 6c7c4a2
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 36 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 9 additions & 5 deletions html/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,11 @@

<script>
// Load data
var data = JSON.parse("{\"ideogram\": {\"columns\": [{\"name\": \"chrom\", \"datatype\": \"String\", \"bit_settings\": \"\", \"values\": [\"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\"]}, {\"name\": \"chromStart\", \"datatype\": \"Int64\", \"bit_settings\": \"\", \"values\": [0, 4200000, 9700000, 17500000, 19000000, 20500000, 25500000, 27800000, 30000000, 30900000, 33400000, 39800000, 42500000, 43300000, 44500000, 49200000, 52600000, 58800000, 59000000, 63400000, 66900000, 67000000, 67200000, 72400000, 74900000, 76300000, 78000000, 81400000, 84700000, 88500000, 93800000, 98000000]}, {\"name\": \"chromEnd\", \"datatype\": \"Int64\", \"bit_settings\": \"\", \"values\": [4200000, 9700000, 17500000, 19000000, 20500000, 25500000, 27800000, 30000000, 30900000, 33400000, 39800000, 42500000, 43300000, 44500000, 49200000, 52600000, 58800000, 59000000, 63400000, 66900000, 67000000, 67200000, 72400000, 74900000, 76300000, 78000000, 81400000, 84700000, 88500000, 93800000, 98000000, 101991189]}, {\"name\": \"name\", \"datatype\": \"String\", \"bit_settings\": \"\", \"values\": [\"p13\", \"p12\", \"p11.2\", \"p11.1\", \"q11.1\", \"q11.2\", \"q12\", \"q13.1\", \"q13.2\", \"q13.3\", \"q14\", \"q15.1\", \"q15.2\", \"q15.3\", \"q21.1\", \"q21.2\", \"q21.3\", \"q22.1\", \"q22.2\", \"q22.31\", \"q22.32\", \"q22.33\", \"q23\", \"q24.1\", \"q24.2\", \"q24.3\", \"q25.1\", \"q25.2\", \"q25.3\", \"q26.1\", \"q26.2\", \"q26.3\"]}, {\"name\": \"gieStain\", \"datatype\": \"String\", \"bit_settings\": \"\", \"values\": [\"gvar\", \"stalk\", \"gvar\", \"acen\", \"acen\", \"gneg\", \"gpos50\", \"gneg\", \"gpos50\", \"gneg\", \"gpos75\", \"gneg\", \"gpos25\", \"gneg\", \"gpos75\", \"gneg\", \"gpos75\", \"gneg\", \"gpos25\", \"gneg\", \"gpos25\", \"gneg\", \"gpos25\", \"gneg\", \"gpos25\", \"gneg\", \"gpos50\", \"gneg\", \"gpos50\", \"gneg\", \"gpos50\", \"gneg\"]}, {\"name\": \"color\", \"datatype\": \"String\", \"bit_settings\": \"\", \"values\": [\"#660099\", \"#6600cc\", \"#660099\", \"#660033\", \"#660033\", \"#ffffff\", \"#808080\", \"#ffffff\", \"#808080\", \"#ffffff\", \"#404040\", \"#ffffff\", \"#c0c0c0\", \"#ffffff\", \"#404040\", \"#ffffff\", \"#404040\", \"#ffffff\", \"#c0c0c0\", \"#ffffff\", \"#c0c0c0\", \"#ffffff\", \"#c0c0c0\", \"#ffffff\", \"#c0c0c0\", \"#ffffff\", \"#808080\", \"#ffffff\", \"#808080\", \"#ffffff\", \"#808080\", \"#ffffff\"]}]}, \"otherVariable\": \"Some other data\"}");
// var data = JSON.parse("{\"ideogram\": {\"columns\": [{\"name\": \"chrom\", \"datatype\": \"String\", \"bit_settings\": \"\", \"values\": [\"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\", \"chr15\"]}, {\"name\": \"chromStart\", \"datatype\": \"Int64\", \"bit_settings\": \"\", \"values\": [0, 4200000, 9700000, 17500000, 19000000, 20500000, 25500000, 27800000, 30000000, 30900000, 33400000, 39800000, 42500000, 43300000, 44500000, 49200000, 52600000, 58800000, 59000000, 63400000, 66900000, 67000000, 67200000, 72400000, 74900000, 76300000, 78000000, 81400000, 84700000, 88500000, 93800000, 98000000]}, {\"name\": \"chromEnd\", \"datatype\": \"Int64\", \"bit_settings\": \"\", \"values\": [4200000, 9700000, 17500000, 19000000, 20500000, 25500000, 27800000, 30000000, 30900000, 33400000, 39800000, 42500000, 43300000, 44500000, 49200000, 52600000, 58800000, 59000000, 63400000, 66900000, 67000000, 67200000, 72400000, 74900000, 76300000, 78000000, 81400000, 84700000, 88500000, 93800000, 98000000, 101991189]}, {\"name\": \"name\", \"datatype\": \"String\", \"bit_settings\": \"\", \"values\": [\"p13\", \"p12\", \"p11.2\", \"p11.1\", \"q11.1\", \"q11.2\", \"q12\", \"q13.1\", \"q13.2\", \"q13.3\", \"q14\", \"q15.1\", \"q15.2\", \"q15.3\", \"q21.1\", \"q21.2\", \"q21.3\", \"q22.1\", \"q22.2\", \"q22.31\", \"q22.32\", \"q22.33\", \"q23\", \"q24.1\", \"q24.2\", \"q24.3\", \"q25.1\", \"q25.2\", \"q25.3\", \"q26.1\", \"q26.2\", \"q26.3\"]}, {\"name\": \"gieStain\", \"datatype\": \"String\", \"bit_settings\": \"\", \"values\": [\"gvar\", \"stalk\", \"gvar\", \"acen\", \"acen\", \"gneg\", \"gpos50\", \"gneg\", \"gpos50\", \"gneg\", \"gpos75\", \"gneg\", \"gpos25\", \"gneg\", \"gpos75\", \"gneg\", \"gpos75\", \"gneg\", \"gpos25\", \"gneg\", \"gpos25\", \"gneg\", \"gpos25\", \"gneg\", \"gpos25\", \"gneg\", \"gpos50\", \"gneg\", \"gpos50\", \"gneg\", \"gpos50\", \"gneg\"]}, {\"name\": \"color\", \"datatype\": \"String\", \"bit_settings\": \"\", \"values\": [\"#660099\", \"#6600cc\", \"#660099\", \"#660033\", \"#660033\", \"#ffffff\", \"#808080\", \"#ffffff\", \"#808080\", \"#ffffff\", \"#404040\", \"#ffffff\", \"#c0c0c0\", \"#ffffff\", \"#404040\", \"#ffffff\", \"#404040\", \"#ffffff\", \"#c0c0c0\", \"#ffffff\", \"#c0c0c0\", \"#ffffff\", \"#c0c0c0\", \"#ffffff\", \"#c0c0c0\", \"#ffffff\", \"#808080\", \"#ffffff\", \"#808080\", \"#ffffff\", \"#808080\", \"#ffffff\"]}]}, \"otherVariable\": \"Some other data\"}");

// Load data


var ideogramData = data.ideogram;
var otherVariable = data.otherVariable;
</script>
Expand Down Expand Up @@ -335,10 +339,6 @@
tooltip.visible = false;
});

graphics.rect(ideoX, ideoY, ideoWidth, ideoHeight);
graphics.stroke({ width: 1, color: 0x333333 });
graphics.fill(0xffffff);

let bandY = ideoY;
let acenSeen = false;
for (let i = ideogramData.columns[0].values.length - 1; i >= 0; i--) {
Expand Down Expand Up @@ -443,6 +443,10 @@
bandY += bandHeight;
}

graphics.rect(ideoX, ideoY, ideoWidth, ideoHeight);
graphics.stroke({ width: 2, color: 0x333333 });
graphics.fill(0xffffff);

app.stage.addChild(graphics);

const chrText = new Text({
Expand Down
87 changes: 57 additions & 30 deletions python/genomeshader/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

from IPython.display import display, HTML
import json
import gzip
import base64

import genomeshader.genomeshader as gs

Expand Down Expand Up @@ -407,48 +409,54 @@ def show_old(
def show(
self,
locus_or_dataframe: Union[str, pl.DataFrame],
vertical: bool = False,
horizontal: bool = False,
group_by: str = None,
):
"""
Visualizes genomic data.
Visualizes genomic data by rendering a graphical representation of a genomic locus.
Args:
locus_or_dataframe (str or DataFrame): Genomic locus to visualize.
Can either be specified as a locus string (e.g. 'chr:start-stop')
or a Polars DataFrame (usually from the get_locus() method,
optionally modified by the user).
width (int, optional): Visualization width. Defaults to 980.
height (int, optional): Visualization height. Defaults to 400.
expand (bool, optional): If True, expands each sample to show all
reads. Defaults to False.
Parameters:
locus_or_dataframe (Union[str, pl.DataFrame]): The genomic locus to visualize, which can be specified as either:
- A string representing the locus in the format 'chromosome:start-stop' (e.g., 'chr1:1000000-2000000').
- A Polars DataFrame containing genomic data, which can be obtained from the `get_locus()` method or created by the user.
horizontal (bool, optional): If set to True, the visualization will be rendered horizontally. Defaults to False.
group_by (str, optional): The name of the column to group data by in the visualization. Defaults to None.
Returns:
None: This method does not return a value; it renders the visualization directly.
"""

if isinstance(locus_or_dataframe, str):
df = self.get_locus(locus_or_dataframe)
reads_df = self.get_locus(locus_or_dataframe)
elif isinstance(locus_or_dataframe, pl.DataFrame):
df = locus_or_dataframe.clone()
reads_df = locus_or_dataframe.clone()
else:
raise ValueError(
"locus_or_dataframe must be a locus string or a" "Polars DataFrame."
"locus_or_dataframe must be a locus string or a Polars DataFrame."
)

ref_chr = df["reference_contig"].min()
ref_start = df["reference_start"].min()
ref_end = df["reference_end"].max()
ref_chr = reads_df["reference_contig"].min()
ref_start = reads_df["reference_start"].min()
ref_end = reads_df["reference_end"].max()

ideo_df = self.ideogram(ref_chr)
ideo_json = ideo_df.write_json()

data_to_pass = {
"ideogram": json.loads(ideo_json),
# Add other variables here as needed
"otherVariable": "Some other data",
# ...
"ref_chr": ref_chr,
"ref_start": ref_start,
"ref_end": ref_end,
}

data_json = json.dumps(data_to_pass)

# Compress JSON data using gzip
compressed_reads = gzip.compress(reads_df.write_json().encode('utf-8'))

# Encode compressed data to base64 to embed in HTML safely
encoded_reads = base64.b64encode(compressed_reads).decode('utf-8')

inner_style = """
body {
display: grid;
Expand Down Expand Up @@ -692,10 +700,24 @@ def show(
"""

inner_data = f"""
import pako from 'https://cdn.skypack.dev/[email protected]';
// Load data
var data = JSON.parse({json.dumps(data_json)});
var ideogramData = data.ideogram;
var otherVariable = data.otherVariable;
window.data = JSON.parse({json.dumps(data_json)});
// Function to decode and parse the JSON
var encodedData = "{encoded_reads}";
// Decompress data
var compressedData = atob(encodedData);
var bytes = new Uint8Array(compressedData.length);
for (var i = 0; i < compressedData.length; i++) {{
bytes[i] = compressedData.charCodeAt(i);
}}
var decompressedData = pako.inflate(bytes, {{ to: 'string' }});
var jsonData = JSON.parse(decompressedData);
console.log(jsonData);
"""

inner_module = """
Expand Down Expand Up @@ -738,7 +760,7 @@ def show(
app.stage.removeChildren();
// Draw all the elements
drawIdeogram(main, ideogramData);
drawIdeogram(main, window.data.ideogram);
// await drawRuler(main);
// await drawTranscripts(main);
}
Expand Down Expand Up @@ -779,10 +801,6 @@ def show(
tooltip.visible = false;
});
graphics.rect(ideoX, ideoY, ideoWidth, ideoHeight);
graphics.stroke({ width: 1, color: 0x333333 });
graphics.fill(0xffffff);
let bandY = ideoY;
let acenSeen = false;
for (let i = ideogramData.columns[0].values.length - 1; i >= 0; i--) {
Expand Down Expand Up @@ -887,6 +905,10 @@ def show(
bandY += bandHeight;
}
graphics.rect(ideoX, ideoY, ideoWidth, ideoHeight);
graphics.stroke({ width: 2, color: 0x333333 });
graphics.fill(0xffffff);
app.stage.addChild(graphics);
const chrText = new Text({
Expand Down Expand Up @@ -1002,8 +1024,10 @@ def show(
newWindow.document.body.appendChild(script);
// Append dataset script
var data = document.createElement('script');
// Append compressed data module
var data = document.createElement('script');
data.type = "module";
data.defer = true;
data.innerHTML = {encoded_data};
newWindow.document.body.appendChild(data);
Expand All @@ -1019,6 +1043,9 @@ def show(
</script>
"""

with open("/Users/kiran/repositories/genomeshader/test.html", "w") as file:
file.write(HTML(html_script).data)

# Display the HTML and JavaScript
display(HTML(html_script))

Expand Down

0 comments on commit 6c7c4a2

Please sign in to comment.