Skip to content

Commit

Permalink
Add tutorial making gene cluster visuals from FASTA files
Browse files Browse the repository at this point in the history
  • Loading branch information
nvelden committed Jan 26, 2024
1 parent d3751c5 commit 45a6e65
Show file tree
Hide file tree
Showing 74 changed files with 15,235 additions and 190 deletions.
12 changes: 7 additions & 5 deletions R/read_fasta.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
#'
#'This function reads protein sequences from the specified FASTA file or all
#'FASTA files within a directory. It specifically looks for metadata in the
#'FASTA headers enclosed in square brackets `[]`, with key-value pairs separated
#'by an equals sign `=`. For example, from the header '>protein1 [gene=scnD]
#'[protein=ScnD]', it extracts 'gene' as the key and 'scnD' as its value, and
#'similarly for other key-value pairs.
#'FASTA headers with key-value pairs separated by an equals sign `=`. For
#'example, from the header '>protein1 [gene=scnD] [protein=ScnD]', it extracts
#''gene' as the key and 'scnD' as its value, and similarly for other key-value
#'pairs.
#'
#'The Biostrings package is required to run this function.
#'
Expand Down Expand Up @@ -80,7 +80,9 @@ read_fasta <- function(fasta_path, sequence = TRUE, keys = NULL, file_extension
fasta_headers <- row.names(sequence_data)

# Find matches
matches <- gregexpr("\\[.*?\\]", fasta_headers, perl = TRUE)
# matches <- gregexpr("\\[.*?\\]", fasta_headers, perl = TRUE)

matches <- gregexpr("(\\w+)=(\\S+)", fasta_headers, perl = TRUE)

no_matches <- which(sapply(matches, function(x) any(x == -1)))

Expand Down
1 change: 1 addition & 0 deletions docs/404.html

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

1 change: 1 addition & 0 deletions docs/LICENSE-text.html

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

1 change: 1 addition & 0 deletions docs/LICENSE.html

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

21 changes: 11 additions & 10 deletions docs/articles/Examples.html

Large diffs are not rendered by default.

514 changes: 514 additions & 0 deletions docs/articles/LoadFastaFiles.html

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions docs/articles/LoadFastaFiles_files/D3-7.8.5/geneviewer.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
dependencies:
- name: geneviewerwidget
version: 0.1.4
src: htmlwidgets
script: geneviewerwidget.js
- name: Themes
version: 0.1.4
src: htmlwidgets
script: ./lib/geneviewer-0.1.4/Themes.js
- name: geneviewer
version: 0.1.4
src: htmlwidgets
script: ./lib/geneviewer-0.1.4/geneviewer.js
- name: D3
version: 7.8.5
src: htmlwidgets
script: ./lib/D3-7.8.5/d3.min.js
stylesheet: ./lib/styles.css

198 changes: 198 additions & 0 deletions docs/articles/LoadFastaFiles_files/D3-7.8.5/geneviewerwidget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
HTMLWidgets.widget({
name: 'geneviewer',
type: 'output',

factory: function (el, width, height) {
var
graphContainer,
style,
data,
links,
series,
titleOptions,
legendOptions;

var widgetId = el.id.split('-')[1];

var draw = function (width, height) {

// Clear out the container if it has anything
d3.select(el).selectAll('*').remove();

el.style["height"] = "100%"

// Apply styles
if (style && typeof style === 'object' && Object.keys(style).length > 0) {
// Apply styles from the style object
for (var key in style) {
if (style.hasOwnProperty(key)) {
el.style[key] = style[key];
}
}
}

// Add Title

if (titleOptions !== null && titleOptions?.height !== null && titleOptions?.show) {

var titleContainer = d3.select(el)
.append("div")
.attr("id", `geneviewer-title-container-${widgetId}`)
.classed("geneviewer-container", true);


titleOptions.width = el.clientWidth
titleOptions.height = computeSize(titleOptions.height, el.clientHeight)

var title = createContainer(
`#geneviewer-title-container-${widgetId}`,
"svg-container",
"titleOptions",
titleOptions)
.title(titleOptions?.title, titleOptions?.subtitle, titleOptions?.show ?? false, titleOptions)
}

// Add legend

var legendHeight = (legendOptions?.show === false) ? 0 : computeSize(legendOptions?.height, el.clientHeight);

if (legendOptions?.group !== null && legendOptions?.show) {

var legendContainer = d3.select(el)
.append("div")
.attr("id", `geneviewer-legend-container-${widgetId}`)
.classed("geneviewer-container", true);

legendOptions.width = width
legendOptions.height = computeSize(legendOptions.height, el.clientHeight)

var legendContainer = createContainer(`#geneviewer-legend-container-${widgetId}`,
"svg-container",
"legendOptions",
legendOptions)
.legendData(data)
.legend(legendOptions?.group ?? false, legendOptions?.show ?? false, el.id, legendOptions);

var legendElement = d3.select(`#geneviewer-legend-container-${widgetId}`).node();
var legendDimensions = legendElement.getBoundingClientRect();
legendHeight = legendDimensions.height;

}

var graph = d3.select(el)
.append("div")
.attr("id", `geneviewer-graph-container-${widgetId}`)
.style("flex-direction", graphContainer["direction"])
.classed("geneviewer-container", true);

// Add Clusters
var clusters = Object.keys(series);
// Add Links
if(links && links.length > 0){
var graphLinks = links.reduce((acc, entry) => {
const convertedData = HTMLWidgets.dataframeToD3(entry.data);
acc = acc.concat(convertedData);
return acc;
}, []);
}

clusters.forEach(function (clusterKey) {

var cluster = series[clusterKey],
containerOptions = cluster.container,
clusterOptions = cluster.cluster,
clusterData = HTMLWidgets.dataframeToD3(series[clusterKey].data),
scaleOptions = cluster.scale,
clusterTitleOptions = cluster.clusterTitle,
footerOptions = cluster.footer,
clusterLabelOptions = cluster.clusterLabel,
labelOptions = cluster.labels,
sequenceOptions = cluster.sequence,
geneOptions = cluster.genes,
coordinateOptions = cluster.coordinates;
scaleBarOptions = cluster.scaleBar;
annotationOptions = cluster.annotations;
trackMouse = cluster.trackMouse;
tooltipOptions = cluster.tooltip;

var clonedContainerOptions = JSON.parse(JSON.stringify(containerOptions));
clonedContainerOptions.height = computeSize(clonedContainerOptions.height, el.clientHeight);
clonedContainerOptions.height -= titleOptions.height ? (titleOptions.height / clusters.length) : 0;
clonedContainerOptions.height -= legendHeight ? (legendHeight / clusters.length) : 0;
clonedContainerOptions.width = computeSize(clonedContainerOptions.width, el.clientWidth);

var clusterLinks = getClusterLinks(graphLinks, clusterKey);

var cluster = createContainer(`#geneviewer-graph-container-${widgetId}`, "svg-container", 'containerOptions', clonedContainerOptions)
.cluster(clusterOptions)
.theme("preset")
.title(clusterTitleOptions?.title, clusterTitleOptions?.subtitle, clusterTitleOptions?.show ?? false, clusterTitleOptions)
.footer(footerOptions?.title, footerOptions?.subtitle, footerOptions?.show ?? false, footerOptions)
.clusterLabel(clusterLabelOptions?.title, clusterLabelOptions?.show ?? false, clusterLabelOptions)
.geneData(data, clusterData)
.scale(scaleOptions)
.sequence(sequenceOptions?.show ?? false, sequenceOptions)
.genes(geneOptions?.group, geneOptions?.show ?? false, geneOptions)
.links(clusterLinks, clusterKey)
.coordinates(coordinateOptions?.show ?? false, coordinateOptions)
.labels(labelOptions?.label, labelOptions?.show ?? false, labelOptions)
.scaleBar(scaleBarOptions?.show ?? false, scaleBarOptions)
.addAnnotations(annotationOptions)
.trackMouse(trackMouse?.show ?? false)
.tooltip(tooltipOptions?.show ?? false, tooltipOptions);

});

// Bottom Legend
if (legendOptions?.position == "bottom" && legendOptions?.show && legendOptions?.group !== null) {

d3.select(`#geneviewer-legend-container-${widgetId}`).remove();

var legendContainer = d3.select(el)
.append("div")
.attr("id", `geneviewer-legend-container-${widgetId}`)
.classed("geneviewer-container", true);

var legendContainer = createContainer(`#geneviewer-legend-container-${widgetId}`,
"svg-container",
"legendOptions",
legendOptions)
.legendData(data)
.legend(legendOptions?.group ?? false, legendOptions?.show ?? false, el.id, legendOptions);

}

};

var addLinks = function(width, height) {

if (!links || links.length === 0) {
return;
}
// Remove all existing links
const graphContainer = d3.select(`#geneviewer-graph-container-${widgetId}`);
//graphContainer.selectAll(".link-marker").remove();

makeLinks(graphContainer, links);

};

return {
renderValue: function (input) {
graphContainer = input.graphContainer;
style = input.style;
data = HTMLWidgets.dataframeToD3(input.data);
links = input.links;
series = input.series;
titleOptions = input.title;
legendOptions = input.legend;
draw(width, height);
addLinks(width, height);
},
resize: function (width, height) {
draw(width, height);
addLinks(width, height);
}
};
}
});

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
const themes = {
preset: {
titleOptions: {
titleFont: {
size: "16px",
style: "normal",
weight: "bold",
fill: "black",
decoration: "normal",
family: "sans-serif",
cursor: "default",
},
subtitleFont: {
fontSize: "12px",
fontWeight: "bold",
fontFamily: "sans-serif",
cursor: "default",
fill: "black"
}
},
footerOptions: {
},
clusterLabelOptions: {
x: 0,
y: 0,
position: 'left',
wrapLabel: true,
wrapOptions: {},
fontSize: "12px",
fontStyle: "normal",
fontWeight: "bold",
fontFamily: "sans-serif",
cursor: "default"
},
sequenceOptions: {
stroke: "grey",
y: 50,
start: null,
stop: null,
strokeWidth: 1,
marker: {
markerHeight: 10,
stroke: "grey",
strokeWidth: 1,
//tiltAmount: -5,
gap: 0
}
},
markerOptions: {
x: 1,
y: 50,
start: null,
stop: null,
size: 15,
colorScheme: null,
customColors: null,
marker: "arrow",
cursor: "default",
stroke: "black",
itemStyle: []
},
genesOptions: {
x: 10,
y: 50,
start: null,
stop: null,
colorScheme: null,
strokeWidth: 1,
cursor: "default",
itemStyle: []
},
tooltipOptions: {
backgroundColor: "white",
triggers: ["markers", "genes", "labels"],
formatter: "<b>Start:</b> {start}<br><b>Stop: {stop}</b>",
opacity: 0,
position: "absolute",
padding: "8px",
borderRadius: "4px",
border: "1px solid rgba(0,0,0,0.1)",
boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)",
pointerEvents: "none",
fontFamily: "Arial, sans-serif",
fontSize: "12px",
zIndex: 1000,
color: "#333",
lineHeight: "1.5"
}
},
genome: {
labelsOptions: {
fontSize: "10px",
show: true
}
}
};

function getMarker(markerName, xPos, yPos, size, height = null) {

height = height === null ? size : height;

switch (markerName) {
case "arrow":
return `M ${xPos} ${yPos - height/2} L ${xPos + size/2} ${yPos + height/2} L ${xPos - size/2} ${yPos + height/2} Z`;
default:
return ""; // Default empty path
}
}
Loading

0 comments on commit 45a6e65

Please sign in to comment.