Commit ffeb391c authored by Lukas Pravda's avatar Lukas Pravda
Browse files

Merge branch 'dev'

parents 1265a35d 6b6a5fca
Pipeline #105124 passed with stages
in 1 minute and 18 seconds
.vscode
.DS_Store
.mypy_cache
node_modules
......
......@@ -12,4 +12,5 @@ pages:
paths:
- public
only:
- dev
- master
......@@ -40,6 +40,9 @@ A few files needs to be imported in the page before the component is attempted t
<!-- CSS style to be used for scene drawing (required for saving SVGs.) -->
<link rel="stylesheet" href="pdb-ligand-env-svg.css" />
<!-- CSS style to be used for the component UI -->
<link rel="stylesheet" href="pdb-ligand-env.css" />
<!-- UI icons -->
<link rel="stylesheet" href="https://ebi.emblstatic.net/web_guidelines/EBI-Icon-fonts/v1.3/fonts.css" />
......@@ -49,7 +52,7 @@ A few files needs to be imported in the page before the component is attempted t
charset="utf-8"></script>
<!--PDBe interactions component-->
<script type="module" src="pdb-ligand-env-component-0.2.0-min.js"></script>
<script type="module" src="pdb-ligand-env-component-0.3.0-min.js"></script>
```
#### A) Ligand interactions
......
......@@ -13,7 +13,7 @@
<script src="https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"
charset="utf-8"></script>
<!--PDBe interactions component-->
<script type="module" src="pdb-ligand-env-component-0.2.0-min.js"></script>
<script type="module" src="pdb-ligand-env-component-0.3.0-min.js"></script>
</head>
......
......@@ -8,8 +8,8 @@
<!-- MOL* -->
<link rel="stylesheet" type="text/css"
href="https://wwwdev.ebi.ac.uk/pdbe/pdb-component-library/v1.0/css/molstar-light-0.0.1.css">
<script src="https://wwwdev.ebi.ac.uk/pdbe/pdb-component-library/v1.0/js/molstar-0.0.1.js"></script>
href="https://www.ebi.ac.uk/pdbe/pdb-component-library/css/pdbe-molstar-1.1.0.css">
<script src="https://www.ebi.ac.uk/pdbe/pdb-component-library/js/pdbe-molstar-plugin-1.1.0.js"></script>
<!-- Web component polyfill (only loads what it needs) -->
<script src="https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs/webcomponents-lite.js" charset="utf-8">
......@@ -18,17 +18,17 @@
<script src="https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"
charset="utf-8"></script>
<script type="module" src="pdb-ligand-env-component-0.2.0-min.js"></script>
<script type="module" src="pdb-ligand-env-component-0.3.0-min.js"></script>
<script>
var renderBmInteractions = function (id, bmId) {
var int =
`<pdb-ligand-env style="border: 1px solid black" pdb-id="${id}" bound-molecule-id="${bmId}"></pdb-ligand-env>`
`<pdb-ligand-env style="border: 1px solid black" pdb-id="${id}" bound-molecule-id="${bmId}" environment="dev"></pdb-ligand-env>`
document.getElementById('rt').innerHTML = int;
};
var renderLigandInteractions = function (id, chain, resId) {
var int =
`<pdb-ligand-env style="border: 1px solid black" pdb-id="${id}" pdb-chain-id="${chain}" pdb-res-id="${resId}" zoom-on></pdb-ligand-env>`
`<pdb-ligand-env style="border: 1px solid black" pdb-id="${id}" pdb-chain-id="${chain}" pdb-res-id="${resId}" zoom-on environment="dev"></pdb-ligand-env>`
document.getElementById('rt').innerHTML = int;
};
......@@ -42,9 +42,9 @@
if (result.bmid === undefined) {
result['query'] =
`https://wwwdev.ebi.ac.uk/pdbe/coordinates/${result.pdbid}/ligandInteraction?&authAsymId=$${result.chain}&authSeqNumber=${result.resid}&radius=5&dataSource=hydrogens`
`https://www.ebi.ac.uk/pdbe/model-server/v1/${result.pdbid}/residueInteraction?auth_asym_id=${result.chain}&auth_seq_id=${result.resid}&radius=5&data_source=pdb-h`
} else {
result['query'] = `https://wwwdev.ebi.ac.uk/pdbe/coordinates/${result.pdbid}?dataSource=hydrogens`
result['query'] = `https://www.ebi.ac.uk/pdbe/model-server/v1/${result.pdbid}/full?data_source=pdb-h`
}
if (result.chain !== undefined) result.chain = result.chain.toUpperCase();
......@@ -64,9 +64,9 @@
} else {
params = {
pdbid: '1cbs',
chain: 'A',
chain: 'A_1',
resid: 200,
query: "https://wwwdev.ebi.ac.uk/pdbe/coordinates/1cbs/ligandInteraction?&authAsymId=$A&authSeqNumber=$200&radius=5&dataSource=hydrogens"
query: "https://www.ebi.ac.uk/pdbe/model-server/v1/1cbs/residueInteraction?auth_asym_id=A_1&auth_seq_id=200&radius=5&data_source=pdb-h"
};
}
......@@ -78,32 +78,27 @@
var initParams = {
moleculeId: params.pdbid,
pdbeUrl: 'https://wwwdev.ebi.ac.uk/pdbe/',
loadMaps: true,
validationAnnotation: true,
domainAnnotation: true,
pdbeUrl: 'https://www.ebi.ac.uk/pdbe/',
loadMaps: false,
bgColor: {
r: 255,
g: 250,
b: 250
},
lowPrecisionCoords: true,
isExpanded: false,
hideControls: true,
showLogs: false,
subscribeEvents: true,
showPDBeLogo: false,
assemblyId: 'preferred', //'deposited'
// selectInteraction: false,
subscribeEvents: true,
ligandView: {
auth_asym_Id: params.chain,
auth_seq_id: params.resid,
auth_asym_id: params.chain,
hydrogens: true
},
backgroundColour: '0xFFFFFF',
//customColorList: [0xff0000, 0x0000ff],
//representationStyle: representationStyle//,
customData: {
url: `https://wwwdev.ebi.ac.uk/pdbe/coordinates/${params.pdbid}/ligandInteraction?&authAsymId=${params.chain}&authSeqNumber=${params.resid}&radius=5&dataSource=hydrogens`,
format: 'cif'
}
}
var pdbeMolstar = new MolStarPdbeWrapper();
var pdbeMolstar = new PDBeMolstarPlugin();
pdbeMolstar.render(document.getElementById('3dViewer'), initParams);
});
}());
......
This diff is collapsed.
{
"name": "pdb-ligand-env",
"version": "0.2.0",
"version": "0.3.0",
"description": "",
"main": "app.js",
"dependencies": {
"@types/d3": "^5.0.1",
"@types/d3": "^5.16.3",
"@types/d3-tip": "^3.5.5",
"d3": "^5.15.0",
"d3": "^5.16.0",
"d3-tip": "^0.9.1",
"d3scription": "^1.0.1",
"lit-element": "^2.2.1",
"typescript": "^3.7.4"
"lit-element": "^2.4.0",
"typescript": "^4.0.2"
},
"devDependencies": {
"@babel/core": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.8.3",
"@babel/preset-env": "^7.8.3",
"@babel/runtime": "^7.8.3",
"@webcomponents/webcomponentsjs": "^2.4.1",
"babel-loader": "^8.0.5",
"browser-sync": "^2.26.7",
"@babel/core": "^7.12.3",
"@babel/plugin-transform-runtime": "^7.12.1",
"@babel/preset-env": "^7.12.1",
"@babel/runtime": "^7.12.1",
"@webcomponents/webcomponentsjs": "^2.5.0",
"babel-loader": "^8.1.0",
"browser-sync": "^2.26.13",
"camelcase": "^5.0.0",
"clean-webpack-plugin": "^1.0.1",
"css-loader": "^2.1.0",
......@@ -32,12 +32,12 @@
"gulp-minify": "^3.1.0",
"live-server": "^1.2.1",
"npm-run-all": "^4.1.3",
"onchange": "^6.1.0",
"onchange": "^6.1.1",
"style-loader": "^0.23.1",
"ts-node": "^7.0.1",
"url-loader": "^1.1.2",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10"
"webpack": "^5.2.0",
"webpack-cli": "^3.3.12"
},
"scripts": {
"tscW": "tsc -w",
......
......@@ -38,10 +38,19 @@ class Depiction {
this.atoms = data.atoms.map(x => new Atom(x));
this.bonds = new Array<Bond>();
let bds = new Set<string>();
data.bonds.forEach(x => {
var atomA = this.atoms.find(e => e.name == x.bgn);
var atomB = this.atoms.find(e => e.name == x.end);
var bond = new Bond(atomA, atomB, x.coords, x.style);
let atomA = this.atoms.find(e => e.name == x.bgn);
let atomB = this.atoms.find(e => e.name == x.end);
let bond = new Bond(atomA, atomB, x.coords, x.style);
let bondFlag = [atomA.name, atomB.name].sort().join("_");
if (!bds.has(bondFlag)) {
bds.add(bondFlag);
atomA.connectivity++;
atomB.connectivity++;
}
this.bonds.push(bond);
});
......@@ -67,16 +76,9 @@ class Depiction {
}
// ideally we want to find an atom which is part just a single bond to get nice initial position.
// If there is no such atom any will do
let searchStruct = new Map<string, number>();
this.bonds.forEach(x => {
searchStruct.set(x.bgn.name, searchStruct.get(x.bgn.name) === undefined ? 1 : searchStruct.get(x.bgn.name) + 1);
searchStruct.set(x.end.name, searchStruct.get(x.end.name) === undefined ? 1 : searchStruct.get(x.end.name) + 1);
});
searchStruct = this.sortMap(searchStruct); // ascending order so we hit those with less partners sooner.
let thisAtomName = [...searchStruct.keys()].find(x => atomNames.findIndex(y => y === x) !== -1)
let thisAtom = this.atoms.find(x => x.name === thisAtomName);
let atoms = this.atoms.filter(x => atomNames.includes(x.name)).sort((x, y) => x.connectivity - y.connectivity);
let thisAtom = atoms[0];
let bond = this.bonds.find(x => x.containsAtom(thisAtom));
let otherAtom = bond.getOtherAtom(thisAtom);
......@@ -88,10 +90,9 @@ class Depiction {
return new Vector2D(x, y);
}
public draw(withClarityNodes: boolean = false) {
public draw() {
this.structure.selectAll("*").remove();
if (withClarityNodes) this.appendClarityNodes();
this.appendBondVisuals();
this.appendTexts();
}
......@@ -154,16 +155,24 @@ class Depiction {
* @memberof Depiction
*/
private appendTexts(): void {
let data = this.atoms
.filter(x => x.labels.length > 0)
?.map(x => x.labels)
?.reduce((a, b) => a.concat(b));
this.structure.selectAll()
.data(this.atoms.filter(x => Object.keys(x.labels).length !== 0).map(x => x.labels).reduce((a, b) => a.concat(b)))
.data(data)
.enter()
.append('text')
.attr('filter', "url(#solid-background)")
.attr('style', (x: any) => x.style)
.attr('x', (x: any) => x.x)
.attr('y', (x: any) => x.y)
.attr('dominant-baseline', (x: any) => x['dominant-baseline'])
.attr('text-anchor', (x: any) => x['text-anchor'])
.each(function (x: any) {
for (var i = 0; i < x.tspans.length; i++) {
d3.select(this)
.append('tspan')
......@@ -173,23 +182,6 @@ class Depiction {
});
}
/**
* Add small white circle on the background of atoms with label
* just to make the interaction lines pretty.
*
* @memberof Depiction
*/
private appendClarityNodes(): void {
this.structure.selectAll()
.data(this.atoms.filter(x => Object.keys(x.labels).length != 0))
.enter().append('circle')
.classed('pdb-lig-env-svg-shadow-node', true)
.attr('cx', (x: any) => x.position.x)
.attr('cy', (x: any) => x.position.y)
.attr('r', 15);
}
public getCenter(ids: string[]): Vector2D {
let coords = new Array<Vector2D>();
......@@ -241,10 +233,13 @@ class Atom {
name: string;
labels: any;
position: Vector2D;
connectivity: number
constructor(item: any) {
this.name = item.name;
this.labels = item.labels;
this.position = new Vector2D(item.x, item.y)
this.position = new Vector2D(item.x, item.y);
this.connectivity = 0;
}
/**
......@@ -347,7 +342,6 @@ class Bond {
coords: string;
style: string;
/**
*Creates an instance of the bond.
* @param {Atom} a
......@@ -358,7 +352,7 @@ class Bond {
this.bgn = a;
this.end = b;
this.coords = coords;
this.style = style.replace("stroke-width:2px", "stroke-width:4px");;
this.style = style.replace("stroke-width:2px", "stroke-width:4px");
}
......
This diff is collapsed.
......@@ -15,14 +15,14 @@ namespace Model {
GroupGroup
}
/**
* What environment should be used
*
* @export
* @enum {number}
*/
export enum Environment {
export enum Environment {
Production,
Development,
Internal
......@@ -149,7 +149,18 @@ namespace Model {
}
public toString(): string {
return `${this.residue.chemCompId} ${this.residue.chainId} ${this.residue.authorResidueNumber}${this.residue.authorInsertionCode}`;
let r = this.residue;
let splitted = r.chainId.split('_');
let chainStr = splitted[0];
let symString = "";
if (splitted.length > 1) {
symString = splitted[1] !== "1"? `[${splitted[1]}]` : "";
}
let str = `${r.chemCompId} | ${chainStr}${symString} | ${r.authorResidueNumber}${r.authorInsertionCode}`;
return str;
}
public toTooltip(): string {
......@@ -157,23 +168,49 @@ namespace Model {
}
}
export interface Link {
source: InteractionNode;
target: InteractionNode;
export abstract class Link {
public source: InteractionNode;
public target: InteractionNode;
constructor(source: InteractionNode, target: InteractionNode) {
this.source = source;
this.target = target;
}
public containsBothNodes(a: InteractionNode, b: InteractionNode): boolean {
let condA = this.source.equals(a) && this.target.equals(b);
let condB = this.source.equals(b) && this.target.equals(a);
return condA || condB;
}
public containsNode(node: InteractionNode) {
return this.source.equals(node) || this.target.equals(node);
}
getLinkClass(): string;
hasClash(): boolean;
toTooltip(): string;
public containsResidue(n: Residue): boolean {
return (this.target.residue.equals(n) || this.source.residue.equals(n));
}
public getOtherNode(node: InteractionNode) {
return this.source.equals(node) ? this.target : this.source;
}
abstract getLinkClass(): string;
abstract hasClash(): boolean;
abstract toTooltip(): string;
}
export class ResidueResidueLink implements Link {
export class ResidueResidueLink extends Link {
target: InteractionNode;
source: InteractionNode
interactions: Map<InteractionType, Array<string>>;
constructor(source: InteractionNode, target: InteractionNode, interactions: any) {
this.source = source;
this.target = target;
super(source, target);
this.interactions = new Map<InteractionType, Array<string>>();
Object.keys(interactions).forEach(x => {
......@@ -182,14 +219,6 @@ namespace Model {
});
}
/**
* Checks if the link contains a given node.
*/
public containsNode(n: InteractionNode): boolean {
return this.target.equals(n) || this.source.equals(n);
}
public hasClash(): boolean {
this.interactions.forEach(x => {
if (x.includes('clash')) {
......@@ -199,17 +228,6 @@ namespace Model {
return false;
}
/**
* Shorthand to check if the link contains particular underlying
* residue.
*/
public containsResidue(n: Residue): boolean {
return (this.target.residue.equals(n) || this.source.residue.equals(n));
}
public isBoundMoleculeLink(): boolean {
return this.source.residue.isLigand && this.target.residue.isLigand && this.interactions.get(InteractionType.AtomAtom).includes('covalent');
}
......@@ -245,30 +263,20 @@ namespace Model {
}
export class LigandResidueLink implements Link {
source: InteractionNode;
target: InteractionNode;
export class LigandResidueLink extends Link {
interaction: Array<Interaction>;
constructor(begin: InteractionNode, end: InteractionNode, beginAtoms: string[], endAtoms: string[],
interactionType: string, interactionDetails: string[], distance: number) {
this.source = begin;
this.target = end;
super(begin, end);
this.interaction = new Array<Interaction>();
this.interaction.push(
new Interaction(beginAtoms, endAtoms, InteractionTypeUtil.parse(interactionType.replace('-', '_')), interactionDetails, distance)
);
}
public containsBothNodes(a: InteractionNode, b: InteractionNode): boolean {
let condA = this.source.equals(a) && this.target.equals(b);
let condB = this.source.equals(b) && this.target.equals(a);
return condA || condB;
}
public addInteraction(beginAtoms: string[], endAtoms: string[], interactionType: string, interactionDetails: string[], distance: number): void {
this.interaction.push(
new Interaction(beginAtoms, endAtoms, InteractionTypeUtil.parse(interactionType.replace('-', '_')), interactionDetails, distance)
......@@ -293,10 +301,6 @@ namespace Model {
return 'other';
}
public containsInteractionNode(node: InteractionNode) {
return this.source.equals(node) || this.target.equals(node);
}
public hasClash(): boolean {
this.interaction.forEach(x => {
x.interactionsClases.forEach(y => {
......@@ -371,6 +375,19 @@ namespace Model {
this.links.push(link);
});
let searchableNodes = Array.from(this.tmpNodesSet);
data.composition.connections.forEach(pair => {
let bgn = searchableNodes.find(x => x.residue.id === pair[0]);
let end = searchableNodes.find(x => x.residue.id === pair[1]);
let result = this.links.find(x => x.containsBothNodes(bgn, end));
if (result === undefined) {
let link = new ResidueResidueLink(bgn, end, { 'atom_atom': ['covalent'] });
this.links.push(link);
}
});
this.interactionNodes = Array.from(this.tmpNodesSet.values());
return this;
......@@ -424,7 +441,7 @@ namespace Model {
return; // we do not want to show 'self interactions'
}
let tmpLink = tmpLinks.find(x => (<LigandResidueLink>x).containsBothNodes(bgnNode, endNode)) as LigandResidueLink;
let tmpLink = tmpLinks.find(x => x.containsBothNodes(bgnNode, endNode));
if (tmpLink !== undefined) {
tmpLink.addInteraction(x.ligand_atoms, x.end.atom_names, x.interaction_type, x.interaction_details, x.distance);
......
......@@ -5,6 +5,7 @@
*/
class ResidueProvider {
private static instance: ResidueProvider;
private environment: Model.Environment;
private mapping: Map<string, string>;
public downloadPromises: Array<Promise<any>>;
......@@ -14,7 +15,8 @@ class ResidueProvider {
* construction calls with the `new` operator.
* @memberof ResidueProvider
*/
private constructor() {
private constructor(env: Model.Environment) {
this.environment = env;
this.mapping = new Map<string, string>(Config.aaAbreviations);
this.downloadPromises = new Array<Promise<any>>();
}
......@@ -30,9 +32,9 @@ class ResidueProvider {
* @returns {ResidueProvider}
* @memberof ResidueProvider
*/
public static getInstance(): ResidueProvider {
public static getInstance(env: Model.Environment = Model.Environment.Production): ResidueProvider {
if (!ResidueProvider.instance) {
ResidueProvider.instance = new ResidueProvider();
ResidueProvider.instance = new ResidueProvider(env);
}
return ResidueProvider.instance;
......@@ -61,7 +63,7 @@ class ResidueProvider {
public downloadAnnotation(r: Model.Residue): void {
if (this.mapping.has(r.chemCompId) || r.isLigand) return;
let url = Resources.residueTypeAPI(r.chemCompId);
let url = Resources.residueTypeAPI(r.chemCompId, this.environment);
let res = d3.json(url).then(x => {
let code = x[r.chemCompId][0].one_letter_code;
this.mapping.set(r.chemCompId, code);
......
namespace Resources {
export const productionAPI: string = 'https://www.ebi.ac.uk/pdbe/graph-api';
export const devAPI: string = 'https://wwwdev.ebi.ac.uk/pdbe/graph-api';
export const intAPI: string = 'https://wwwint.ebi.ac.uk/pdbe/graph-api';
export const productionAPI: string = 'https://www.ebi.ac.uk/pdbe';
export const devAPI: string = 'https://wwwdev.ebi.ac.uk/pdbe';
export const intAPI: string = 'https://wwwint.ebi.ac.uk/pdbe';
// export const glycanSymbols: string = 'https://pdbe.gitdocs.ebi.ac.uk/web-components/ligand-env/pdb-snfg-visuals.xml';
// export const glycanMapping: string = 'https://pdbe.gitdocs.ebi.ac.uk/web-components/ligand-env/het_mapping.json';
// export const componentSvgCss: string = 'https://pdbe.gitdocs.ebi.ac.uk/web-components/ligand-env/pdb-ligand-env-svg.css';
export const boundMoleculeURL: string = 'graph-api/pdb/bound_molecule_interactions';
export const carbohydrateURL: string = 'graph-api/pdb/carbohydrate_polymer_interactions';
export const boundLigandURL: string = 'graph-api/pdb/bound_ligand_interactions';
export const compoundSummaryURL: string = 'api/pdb/compound/summary';
export const componentLibraryURL: string = 'pdb-component-library/data/ligand-env';
export const staticFilesURL: string = 'static/files/pdbechem_v2';
export const glycanSymbols: string = 'https://www.ebi.ac.uk/pdbe/pdb-component-library/data/ligand-env/pdb-snfg-visuals.xml';
export const glycanMapping: string = 'https://www.ebi.ac.uk/pdbe/pdb-component-library/data/het_mapping.json';
export const componentSvgCss: string = 'https://www.ebi.ac.uk/pdbe/pdb-component-library/data/pdb-ligand-env-svg.css';
export function glycanSymbolsAPI(env: Model.Environment) {
let url = '';