Skip to content

Commit

Permalink
Merge pull request #342 from vgteam/colorNodes
Browse files Browse the repository at this point in the history
Color nodes
  • Loading branch information
adamnovak authored Sep 27, 2023
2 parents 0168f2d + 88de97e commit 429fb48
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 8 deletions.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ This will save some time during the interactive visualization, especially if the

The net result needs to be one or more chunk directories on disk, referenced from a BED file.

To generate each chunk, you can use the `prepare_chunks.sh` script. You ought to run it from the directory containing your input files and where your output chunks will be stored (i.e. the `dataPath` in `sequenceTubeMpas/src/config.json`), which defaults to the `exampleData` directory in the repo.
To generate each chunk, you can use the `prepare_chunks.sh` script. You ought to run it from the directory containing your input files and where your output chunks will be stored (i.e. the `dataPath` in `sequenceTubeMaps/src/config.json`), which defaults to the `exampleData` directory in the repo.

For example:

Expand All @@ -162,6 +162,23 @@ Note each column is seperated by tabs

This BED file needs to be in the `dataPath` directory, or it can be hosted on the web along with its chunk directories and accessed via URL.

If you want certain nodes of the graph to be colored, place the node names to be colored in a `nodeColors.tsv` file, with a node name on each line, within output directory of the chunk. When rendered, these specified nodes will be colored differently than other nodes.

You can use `prepare_chunks.sh` script to generate this additional `nodeColors.tsv` by adding an additional option. Here is an example:

```
cd exampleData/
../scripts/prepare_chunk.sh -x mygraph.xg -h mygraph.gbwt -r chr1:1-100 -d 'Region A' -o chunk-chr1-1-100 -g mygam1.gam -g mygam2.gam -n "1 2 3" >> mychunks.bed
```

Adding this additional `n` flag will allow a string space delimited input of node names which will be outputted to `nodeColors.tsv`.

```
1
2
3
```

##### Pre-made subgraphs

You may want to look at a graph that has already been extracted from a larger graph.
Expand Down
13 changes: 12 additions & 1 deletion scripts/prepare_chunks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ function usage() {
exit 1
}

while getopts x:h:g:r:o:d: flag
while getopts x:h:g:r:o:d:n: flag
do
case "${flag}" in
x) GRAPH_FILE=${OPTARG};;
Expand All @@ -17,6 +17,7 @@ do
r) REGION=${OPTARG};;
o) OUTDIR=${OPTARG};;
d) DESC="${OPTARG}";;
n) NODE_COLORS="${OPTARG}";;
*)
usage
;;
Expand Down Expand Up @@ -56,6 +57,7 @@ echo >&2 "Graph File: " $GRAPH_FILE
echo >&2 "Haplotype File: " $HAPLOTYPE_FILE
echo >&2 "Region: " $REGION
echo >&2 "Output Directory: " $OUTDIR
echo >&2 "Node colors: " $NODE_COLORS

rm -fr $OUTDIR
mkdir -p $OUTDIR
Expand All @@ -78,6 +80,15 @@ for GAM_FILE in "${GAM_FILES[@]}"; do
vg_chunk_params+=(-a $GAM_FILE)
done

# construct node file
if [[ ! -z "${NODE_COLORS}" ]] ; then
node_names_arr=($NODE_COLORS);
for NODENAME in "${node_names_arr[@]}"; do
echo >&2 "$NODENAME"
printf "$NODENAME\n" >> $OUTDIR/nodeColors.tsv
done
fi

# Call vg chunk
vg chunk "${vg_chunk_params[@]}" > $OUTDIR/chunk.vg

Expand Down
13 changes: 12 additions & 1 deletion scripts/prepare_local_chunk.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ function usage() {
exit 1
}

while getopts x:g:r:o:d: flag
while getopts x:g:r:o:d:n: flag
do
case "${flag}" in
x) GRAPH_FILE=${OPTARG};;
g) GAM_FILES+=("$OPTARG");;
r) REGION=${OPTARG};;
o) OUTDIR=${OPTARG};;
d) DESC="${OPTARG}";;
n) NODE_COLORS="${OPTARG}";;
*)
usage
;;
Expand Down Expand Up @@ -54,6 +55,7 @@ fi
echo >&2 "Graph File: " $GRAPH_FILE
echo >&2 "Region: " $REGION
echo >&2 "Output Directory: " $OUTDIR
echo >&2 "Node colors: " $NODE_COLORS

rm -fr $OUTDIR
mkdir -p $OUTDIR
Expand Down Expand Up @@ -92,6 +94,15 @@ for GAM_FILE in "${GAM_FILES[@]}"; do
GAM_NUM=$((GAM_NUM + 1))
done

# construct node file
if [[ ! -z "${NODE_COLORS}" ]] ; then
node_names_arr=($NODE_COLORS);
for NODENAME in "${node_names_arr[@]}"; do
echo >&2 "$NODENAME"
printf "$NODENAME\n" >> $OUTDIR/nodeColors.tsv
done
fi

# Make the empty but required annotation file. We have no haplotypes to put in it.
touch "${OUTDIR}/chunk_0_${REGION_CONTIG}_${REGION_START}_${REGION_END}.annotate.txt"
printf "\tchunk_0_${REGION_CONTIG}_${REGION_START}_${REGION_END}.annotate.txt\n" >> $OUTDIR/regions.tsv
Expand Down
1 change: 1 addition & 0 deletions src/components/TubeMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class TubeMap extends Component {
tubeMap.setTransparentNodesFlag(visOptions.transparentNodes);
tubeMap.setShowReadsFlag(visOptions.showReads);
tubeMap.setSoftClipsFlag(visOptions.showSoftClips);
tubeMap.setColoredNodes(visOptions.coloredNodes);

for (let key of Object.keys(visOptions.colorSchemes)) {
// Apply color-by-mapping-quality parameter to all the schemes.
Expand Down
5 changes: 4 additions & 1 deletion src/components/TubeMapContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class TubeMapContainer extends Component {
tracks={this.state.tracks}
reads={this.state.reads}
region={this.state.region}
visOptions={this.props.visOptions}
visOptions={{coloredNodes: this.state.coloredNodes, ...this.props.visOptions}}
/>
</div>
</div>
Expand Down Expand Up @@ -188,13 +188,16 @@ class TubeMapContainer extends Component {
const reads = readsArr.flat();

const region = json.region;
const coloredNodes = json.coloredNodes;
this.setState({
isLoading: false,
nodes,
tracks,
reads,
region,
coloredNodes
});

}
} catch (error) {
this.handleFetchError(
Expand Down
38 changes: 38 additions & 0 deletions src/server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,43 @@ function processRegionFile(req, res, next) {

lineReader.on("close", () => {
console.timeEnd("processing region file");
processNodeColorsFile(req, res, next);
});
} catch (error) {
return next(error);
}
}

function processNodeColorsFile(req, res, next) {
try {
console.time("processing node colors file");
const nodeColorsFile = `${req.chunkDir}/nodeColors.tsv`;
if (!isAllowedPath(nodeColorsFile)) {
throw new BadRequestError(
"Path to node colors file not allowed: " + nodeColorsFile
);
}

req.coloredNodes = [];

// check if file exists
if (!fs.existsSync(nodeColorsFile)) {
cleanUpAndSendResult(req, res, next);
return;
}

const lineReader = rl.createInterface({
input: fs.createReadStream(nodeColorsFile),
});

lineReader.on("line", (line) => {
console.log("Node name: " + line);
const nodeName = line.replace("\n", "");
req.coloredNodes.push(nodeName);
});

lineReader.on("close", () => {
console.timeEnd("processing node colors file");
cleanUpAndSendResult(req, res, next);
});
} catch (error) {
Expand Down Expand Up @@ -996,6 +1033,7 @@ function cleanUpAndSendResult(req, res, next) {
result.graph = req.graph;
result.gam = req.withGam === true ? req.gamResults : [];
result.region = req.region;
result.coloredNodes = req.coloredNodes;
res.json(result);
console.timeEnd("request-duration");
} catch (error) {
Expand Down
32 changes: 28 additions & 4 deletions src/util/tubemap.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ const config = {
},
};





// variables for storing info which can be directly translated into drawing instructions
let trackRectangles = [];
let trackCurves = [];
Expand Down Expand Up @@ -386,6 +390,10 @@ export function setNodeWidthOption(value) {
}
}

export function setColoredNodes(value){
config.coloredNodes = value;
}

// sets callback function that would generate React popup of track information. The callback would
// accept an array argument of track attribute pairs containing attribute name as a string and attribute value
// as a string or number, to be displayed.
Expand Down Expand Up @@ -3132,7 +3140,7 @@ function drawNodes(dNodes, groupNode) {
}
});

//let nodeGroup = svg.append("g").attr("class", "node")
console.log("config:", config)

groupNode
.selectAll("node")
Expand All @@ -3145,14 +3153,30 @@ function drawNodes(dNodes, groupNode) {
.on("mouseout", nodeMouseOut)
.on("dblclick", nodeDoubleClick)
.on("click", nodeSingleClick)
.style("fill", config.transparentNodesFlag ? "none" : "#fff")
.style("fill-opacity", config.showExonsFlag ? "0.4" : "0.6")
.style("stroke", "black")
.style("fill", (d) => colorNodes(d.name)["fill"])
.style("fill-opacity", (d) => colorNodes(d.name)["fill-opacity"])
.style("stroke", (d) => colorNodes(d.name)["outline"])
.style("stroke-width", "2px")
.append("svg:title")
.text((d) => getPopUpNodeText(d));
}

// Given a node name, return an object with "fill", "fill-opacity", and "outline"
// keys describing what colors should be used to draw it.
function colorNodes(nodeName){
let nodesColors = {};
if (config.coloredNodes.includes(nodeName)){
nodesColors["fill"] = "#ffc0cb"
nodesColors["fill-opacity"] = "0.4"
nodesColors["outline"] = "#ff0000"
} else {
nodesColors["fill"] = "#ffffff"
nodesColors["fill-opacity"] = "0.4"
nodesColors["outline"] = "#000000"
}
return nodesColors;
}

function getPopUpNodeText(node) {
return `Node ID: ${node.name}` + (node.switched ? ` (reversed)` : ``) + `\n`;
}
Expand Down

0 comments on commit 429fb48

Please sign in to comment.