Skip to content
Snippets Groups Projects
Commit 5325a850 authored by Ibrahim Roshan Kunnakkattu's avatar Ibrahim Roshan Kunnakkattu
Browse files

added features to show aggregated ligand atom interactions

parent 1e472b01
No related branches found
No related tags found
2 merge requests!5Refactored code for diplaying aggregated ligand interactions,!4Added features for aggregated ligand interaction view and interactivity with heat map
......@@ -13,6 +13,7 @@ class pdbLigandEnv extends LitElement {
entityId: { type: String, attribute: 'entity-id' },
resName: { type: String, attribute: 'pdb-res-name' },
resId: { type: Number, attribute: 'pdb-res-id' },
contactType: {type: String, attribute: 'contact-type'},
chainId: { type: String, attribute: 'pdb-chain-id' },
substructureHighlight: { type: Array, attribute: 'substructure' },
substructureColor: { type: String, attribute: 'color' },
......@@ -47,12 +48,22 @@ class pdbLigandEnv extends LitElement {
this.display.initLigandInteractions(this.pdbId, this.resId, this.chainId);
}
}
else if (this.resName) {
this.display.initLigandDisplay(this.resName, names).then(() => this.display.centerScene());
else if (this.resName){
this.display.initLigandDisplay(this.resName, names).then(() => {
if (this.contactType){
if(this.display.atomWeights === undefined){
this.display.initLigandWeights(this.resName).then(() => {
this.display.showWeights(this.contactType);
})
}
else {
this.display.showWeights(this.contactType);
}
}
})
}
}
//#region properties
set depiction(data) {
if (!data) return;
......@@ -61,6 +72,10 @@ class pdbLigandEnv extends LitElement {
this.display.centerScene();
}
set atomWeights(contactType) {
this.display.showWeights(contactType);
}
set highlightSubstructure(data) {
if (!data || !this.display) {
console.log(`Argument needs to be a non empty array of strings.`);
......@@ -78,12 +93,6 @@ class pdbLigandEnv extends LitElement {
this.display.addLigandHighlight(this.substructureHighlight, this.highlightColor);
}
set contourData(data) {
if (!data || !this.display || !this.display.depiction !== undefined) return;
this.display.addContours(data);
}
set zoom(data) {
if (this.display !== undefined) this.display.toggleZoom(data);
}
......@@ -104,4 +113,4 @@ class pdbLigandEnv extends LitElement {
}
// Register the new element with the browser.
customElements.define('pdb-ligand-env', pdbLigandEnv);
\ No newline at end of file
customElements.define('pdb-ligand-env', pdbLigandEnv);
......@@ -7,6 +7,9 @@ namespace Config {
export const interactionShowLabelEvent: string = 'PDB.interactions.showLabel';
export const interactionHideLabelEvent: string = 'PDB.interactions.hideLabel';
export const LigandShowAtomEvent: string = 'PDB.ligand.showAtom';
export const LigandHideAtomEvent: string = 'PDB.ligand.hideAtom';
export const molstarClickEvent: string = 'PDB.molstar.click';
export const molstarMouseoverEvent: string = 'PDB.molstar.mouseover';
export const molstarMouseoutEvent: string = 'PDB.molstar.mouseout';
......
......@@ -13,24 +13,25 @@
* @param {Vector2D} resolution x,y dimension of the image. Needs to be used
* for a scene shift, so it is centered.
*/
class Depiction {
ccdId: string;
atoms: Atom[];
bonds: Bond[];
resolution: Vector2D;
private parent: HTMLElement;
private root: d3.Selection<SVGGElement, unknown, null, undefined>;
private structure: d3.Selection<SVGGElement, unknown, null, undefined>;
private contour: d3.Selection<SVGGElement, unknown, null, undefined>;
private highlight: d3.Selection<SVGGElement, unknown, null, undefined>;
public weight: d3.Selection<SVGGElement, unknown, null, undefined>;
private structure: d3.Selection<SVGGElement, unknown, null, undefined>;
constructor(parent: any, data: any) {
this.root = parent
constructor(parent: any, root: any, data: any) {
this.root = root;
this.parent = parent;
this.highlight = this.root.append('g').attr('id', 'highlight');
this.weight = this.root.append('g').attr('id', 'weight');
this.structure = this.root.append('g').attr('id', 'structure');
this.contour = this.root.append('g').attr('id', 'contour');
this.ccdId = data.ccd_id;
this.resolution = new Vector2D(data.resolution.x, data.resolution.y);
......@@ -53,7 +54,9 @@ class Depiction {
}
this.bonds.push(bond);
});
}
......@@ -62,7 +65,7 @@ class Depiction {
* atom.
*
* Present implementation sorts all the partners based on the atom
* degree and then gets the one with the lovest degree and places
* degree and then gets the one with the lowest degree and places
* the initial residue position along the vector pointing from it.
*
* @param {string[]} atomNames list of atom names the bound residue
......@@ -125,13 +128,124 @@ class Depiction {
.attr('d', x => `M ${x.bgn.position.x},${x.bgn.position.y} ${x.end.position.x},${x.end.position.y}`)
.attr('style', `fill:none;fill-rule:evenodd;stroke:${color};stroke-width:22px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1`)
}
/**
* Adds circles around atoms corresponding to the value of
* their weights.The size, color and number of crcles around
* atom indicates the strenght of weight. Currently maximum of
* three circles are drawn
* @param {any} weights objects with atom names and value of weight
* @param {string} colorScheme Name of color to be used for circles
* @memberof Depiction
*/
public addContour(data: any) {
this.contour.selectAll('*').remove();
public addCircles(weights: any, colorScheme: string): void {
this.atoms.forEach(x => {
x.value = weights.filter(y => y.atom == x.name).map(z => z.value)[0]
this.contour.append('div').text(`'contour data goes here: ${data}`);
})
const data = this.atoms.filter(x => x.value >0);
const intxWeights = weights.map(x => x.value);
const color = d3[`scheme${colorScheme}`][9]
const q1 = d3.quantile(intxWeights, 0.25);
const q3 = d3.quantile(intxWeights, 0.75);
const IntxWeightMax = Number(d3.max(intxWeights))
const firstRadiusScale = d3.scaleLinear([0, IntxWeightMax], [20, 30]);
const firstColorScale = d3.scaleLinear([0,IntxWeightMax], ["#FFFFFF", color[4]]);
const secondRadiusScale = d3.scaleLinear([0,IntxWeightMax], [10, 20]);
const secondColorScale = d3.scaleLinear([0, IntxWeightMax], [color[5], color[7]])
const thirdRadiusScale = d3.scaleLinear([0, IntxWeightMax], [2, 10]);
const thirdColorScale = d3.scaleLinear([0, IntxWeightMax], [color[8], color[9]]);
this.weight.selectAll()
.data(data)
.enter()
.each(function(x: any){
if(x.value >= q1){
d3.select(this)
.append('circle')
.attr('cx', x.position.x)
.attr('cy', x.position.y)
.attr('r', secondRadiusScale(x.value))
.attr('fill', secondColorScale(x.value))
.attr("fill-opacity", "0.5")
if(x.value >= q3){
d3.select(this)
.append('circle')
.attr('cx', x.position.x)
.attr('cy', x.position.y)
.attr('r', thirdRadiusScale(x.value))
.attr('fill', thirdColorScale(x.value))
.attr("fill-opacity", "0.5")
}
}
});
this.weight.selectAll()
.data(data)
.enter()
.append('circle')
.attr("cx", x => x.position.x)
.attr("cy", x => x.position.y)
.attr("r", x => firstRadiusScale(x.value))
.attr("fill", x=> firstColorScale(x.value))
.attr("fill-opacity", "0.5")
.on('mouseenter', (x:Atom) => this.atomMouseEnterEventHandler(x))
.on('mouseleave', () => this.atomMouseLeaveEventHandler());
}
// private atomScale(i: number, g:any){
// let selectedAtom = d3.select(g[i]);
// selectedAtom
// .transition()
// .duration(100)
// .attr("r", Number(selectedAtom.attr("r")) * 1.2);
// }
// private atomShrink(i: number, g:any){
// let selectedAtom = d3.select(g[i]);
// selectedAtom
// .transition()
// .duration(100)
// .attr("r", Number(selectedAtom.attr("r")) / 1.2);
// }
private atomMouseEnterEventHandler(x: Atom){
// this.atomScale(i, g)
this.fireExternalAtomEvent(x, Config.LigandShowAtomEvent);
}
private atomMouseLeaveEventHandler(){
// this.atomShrink(i, g);
this.fireExternalNullEvent(Config.LigandHideAtomEvent);
}
private fireExternalAtomEvent(atom: Atom, eventName: string){
const e = new CustomEvent(eventName, {
bubbles: true,
detail: {
tooltip: atom.toTooltip()
}
});
this.parent.dispatchEvent(e);
}
private fireExternalNullEvent(eventName: string) {
const e = new CustomEvent(eventName, {
bubbles: true,
detail: {}
});
this.parent.dispatchEvent(e);
}
/**
* Appends to a given selection the visual representation of bonds as svg:path elements.
*
......@@ -183,7 +297,7 @@ class Depiction {
.data(data)
.enter()
.append('g')
.attr('filter', 'url(#solid-background)')
.attr('filter', 'labels')
.each(function (x: any) {
for (var i = 0; i < x.labels.length; i++) {
d3.select(this)
......@@ -247,13 +361,15 @@ class Atom {
name: string;
labels: any;
position: Vector2D;
connectivity: number
connectivity: number;
value: number;
constructor(item: any) {
this.name = item.name;
this.labels = item.labels;
this.position = new Vector2D(item.x, item.y);
this.connectivity = 0;
this.value = 0;
}
/**
......@@ -263,11 +379,19 @@ class Atom {
* @returns true if the atoms are equal
* @memberof Atom
*/
public equals(other: Atom) {
public equals(other: Atom): boolean {
if (!(other instanceof Atom)) return false;
return other.name === this.name;
}
/**
* @return {string} name of atom
* @memberof Atom
*/
public toTooltip(): string {
return `<span>${this.name} | ${this.value}</span>`;
}
}
/**
......@@ -340,7 +464,6 @@ class Vector2D {
}
}
/**
* Represents a bond in a 2D depiction.
*
......
......@@ -26,6 +26,7 @@ class Visualization {
private visualsMapper: VisualsMapper;
private interactionsData: any;
private atomWeights: any;
private selectedResidueHash: string;
private nodeDragged: boolean;
......@@ -272,6 +273,21 @@ class Visualization {
.then(() => this.centerScene());
}
/**
* Download and set atom weights from protein-ligand interactions data.
*
* @param {string} ligandId
* @memberof Visualization
*/
public async initLigandWeights(ligandId: string){
const weightUrl = `https://raw.githubusercontent.com/roshkjr/Learning-sources/main/${ligandId}_atom_intx.json`
return d3.json(weightUrl)
.then((d: any) => this.atomWeights = d)
.catch(e => console.log(e));
}
/**
* Add depiction to the canvas from external resource.
......@@ -281,10 +297,26 @@ class Visualization {
* @memberof Visualization
*/
public addDepiction(depiction: any, withNames: boolean) {
this.depiction = new Depiction(this.depictionRoot, depiction);
this.depiction = new Depiction(this.parent, this.depictionRoot, depiction);
this.depiction.draw(withNames);
}
/**
* Adds circles around atoms higlighting the weights of atoms.
*
* @param {string} contactType
* @memberof Visualization
*/
public showWeights(contactType: string){
if ((this.depiction === undefined) || (this.atomWeights) === undefined || !(contactType in this.atomWeights)) return;
const weight = this.atomWeights[contactType];
const colorScheme = "Greens";
this.depiction.addCircles(weight, colorScheme);
if(this.zoomHandler !== undefined){
this.zoomHandler(this.svg, d3.zoomIdentity)
};
}
/**
* Show depiction with/without atom names
......@@ -312,21 +344,10 @@ class Visualization {
* @memberof Visualization
*/
public addLigandHighlight(highlight: string[], color: string = undefined) {
if (!this.depiction) return;
this.depiction.highlightSubgraph(highlight, color);
}
/**
* Add contours to the ligand structure. The previous contours are
* going to be removed.
*
* @param {*} data
* @memberof Visualization
*/
public addContours(data: any) {
this.depiction.addContour(data);
}
/**
* Add ligand interactions to the canvas
*
......@@ -611,6 +632,7 @@ class Visualization {
this.parent.dispatchEvent(e);
}
// #endregion fire events
......
......@@ -114,6 +114,9 @@ let helpBonds = `
// #endregion help
/**
* @class UI
*/
class UI {
......@@ -263,6 +266,8 @@ class UI {
this.parent.addEventListener(Config.interactionClickEvent, e => this.nodeMouseEnterEventHandler(e));
this.parent.addEventListener(Config.interactionMouseoverEvent, e => this.nodeMouseEnterEventHandler(e));
this.parent.addEventListener(Config.interactionMouseoutEvent, () => this.nodeMouseLeaveEventHandler());
this.parent.addEventListener(Config.LigandShowAtomEvent, e => this.nodeMouseEnterEventHandler(e));
this.parent.addEventListener(Config.LigandHideAtomEvent, () => this.nodeMouseLeaveEventHandler());
}
if (this.residueLabel !== undefined) {
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment