diff --git a/inspect/list_entities/index.html b/inspect/list_entities/index.html new file mode 100644 index 0000000..7c0b7b5 --- /dev/null +++ b/inspect/list_entities/index.html @@ -0,0 +1,14 @@ + + + Mol* Gallery + + + + +
+ +
+ + + + diff --git a/inspect/list_entities/package.json b/inspect/list_entities/package.json new file mode 100644 index 0000000..e58ba21 --- /dev/null +++ b/inspect/list_entities/package.json @@ -0,0 +1,24 @@ +{ + "name": "molstar-typescript-example", + "version": "1.0.0", + "description": "Molstar and TypeScript example starter project", + "main": "index.html", + "scripts": { + "start": "parcel index.html", + "build": "parcel build index.html" + }, + "dependencies": { + "parcel-bundler": "1.12.5", + "molstar": "4.3.0" + }, + "devDependencies": { + "typescript": "4.4.4" + }, + "resolutions": { + "@babel/preset-env": "7.13.8" + }, + "keywords": [ + "typescript", + "molstar" + ] + } \ No newline at end of file diff --git a/inspect/list_entities/src/common/init.ts b/inspect/list_entities/src/common/init.ts new file mode 100644 index 0000000..3b5dbaa --- /dev/null +++ b/inspect/list_entities/src/common/init.ts @@ -0,0 +1,19 @@ +import { PluginContext } from "molstar/lib/mol-plugin/context"; +import { DefaultPluginSpec } from "molstar/lib/mol-plugin/spec"; + +export async function createRootViewer() { + const viewport = document.getElementById("app") as HTMLDivElement; + const canvas = document.getElementById("canvas") as HTMLCanvasElement; + + const plugin = new PluginContext(DefaultPluginSpec()); + await plugin.init(); + + if (!plugin.initViewer(canvas, viewport)) { + viewport.innerHTML = "Failed to init Mol*"; + throw new Error("init failed"); + } + //@ts-ignore + window["molstar"] = plugin; + + return plugin; +} diff --git a/inspect/list_entities/src/index.ts b/inspect/list_entities/src/index.ts new file mode 100644 index 0000000..393d5c6 --- /dev/null +++ b/inspect/list_entities/src/index.ts @@ -0,0 +1,120 @@ +import { Queries, QueryContext, StructureProperties, StructureSelection } from "molstar/lib/mol-model/structure"; +import { createRootViewer } from "./common/init"; +import { StructureSelectionQueries, StructureSelectionQuery } from "molstar/lib/mol-plugin-state/helpers/structure-selection-query"; +import { MolScriptBuilder as MS } from 'molstar/lib/mol-script/language/builder'; +import { Location } from "molstar/lib/mol-model/structure/structure/element/location"; + + +async function init() { + // Create viewer + const plugin = await createRootViewer(); + + // Download PDB + const fileData = await plugin.builders.data.download( + { url: "https://models.rcsb.org/1PTH.bcif", isBinary: true } + ); + + // Load PDB and create representation + const trajectory = await plugin.builders.structure.parseTrajectory(fileData, "mmcif"); + const presetStateObjects = await plugin.builders.structure.hierarchy.applyPreset(trajectory, "default"); + + if (!presetStateObjects) { + throw new Error("Structure not loaded"); + } + + // Get Structure object from the structure stateObject selector. + // The Structure object contains properties and accessors to the underlying molecular data such as chains, residues, atoms, etc. + const struct = presetStateObjects.structure.data!; + + // Create a QueryContext to be reused for all queries + // Limits the queries to only look at the structure + const ctx = new QueryContext(struct) + + + // ==== Number of Waters ==== + // Created a query to select all residues that are water + // but only select 1 atom per water (ensuring a Singleton selection) + const waterQuery = Queries.generators.atoms({ + 'entityTest': ctx => StructureProperties.entity.type(ctx.element) === 'water' + }) + // Since we used Queries.generators.atoms, our selection will by grouped by Atom and is therefore Singletons + const waterSelection = waterQuery(ctx) as StructureSelection.Singletons; + const numWaters = waterSelection.structure.atomicResidueCount; + + + // ==== Covalent ligand names and residue code ==== + // Create a query expression for all ligands connected to the protein + const covalentLigandExp = MS.struct.filter.isConnectedTo({ + 0: StructureSelectionQueries.ligand.expression, // All ligands + target: StructureSelectionQueries.protein.expression, // All protein atoms + 'bond-test': true // Only atoms covalently bound to the protein + }) + // Query the atoms with the context to get a StructureSelection + const covLigQuery = StructureSelectionQuery('only-covalent-ligands', covalentLigandExp).query; + const covLigSelection = covLigQuery(ctx); + // Assume ligands in structure have >1 atoms. + // Therefore, the StructureSelection must be a Sequence + // If the selection is empty, set the ligand structures to an empty array + const covLigStructures = StructureSelection.isEmpty(covLigSelection) ? [] : (covLigSelection as StructureSelection.Sequence).structures; + // Retrieve each ligand name and residue code + const covLigNames: string[] = []; + const covLigRes: string[] = []; + const auxCovLigLocation = Location.create(); // Create a Location object to retrieve properties + covLigStructures.forEach(s => { + auxCovLigLocation.structure = s; // Set the structure for the location + s.units.map(u => { + // Set the Location to the first element of the ligand + auxCovLigLocation.unit = u; + auxCovLigLocation.element = u.elements[0]; + // Use the Location to query the ligand name property of the ligand + const name = StructureProperties.entity.pdbx_description(auxCovLigLocation).join('|') + covLigNames.push(name); + s// Use the Location to query the reidue code for the ligand + const res = StructureProperties.atom.label_comp_id(auxCovLigLocation) + covLigRes.push(res); + }) + }) + + + // ==== Number of AltLoc positions ==== + const altLocQuery = Queries.generators.atoms({ + // Any atom with a non '' alt_id + 'atomTest': ctx => !!StructureProperties.atom.label_alt_id(ctx.element), + }); + // Since we used Queries.generators.atoms, our selection will by grouped by Atom and is therefore Singletons + const altLocSelection = altLocQuery(ctx) as StructureSelection.Singletons; + const numAltLocs = altLocSelection.structure.elementCount; + + + // ==== Polymer ASYM Unit name and chain ==== + const polymerSelection = StructureSelectionQueries.polymer.query(ctx) + // Polymer query groups selected atoms by entity. Assume it creates a Sequence StructureSelection + const polymerStructues = (polymerSelection as StructureSelection.Sequence).structures; + // Iterate over each polymer unit in each structure and get the name and chain + const namePolymers: string[] = []; + const chainPolymers: string[] = []; + const auxPolymerLocation = Location.create(); // Create a Location object to retrieve properties + polymerStructues.forEach(s => { + auxPolymerLocation.structure = s; // Set the structure for the location + s.units.map(u => { + // Set the Location to the first element of the polymer + auxPolymerLocation.unit = u; + auxPolymerLocation.element = u.elements[0]; + // Use the Location to query the polymer name and chain + const name = StructureProperties.entity.pdbx_description(auxPolymerLocation).join('|') + namePolymers.push(name); + const chain = StructureProperties.chain.auth_asym_id(auxPolymerLocation); + chainPolymers.push(chain); + }) + }) + + console.table([ + {title: 'Water count', value: numWaters}, + {title: 'Covalent Ligand name', value: covLigNames.join(', ')}, + {title: 'Covalent Ligand residue', value: covLigRes.join(', ')}, + {title: 'Alt Loc Count', value: numAltLocs}, + {title: 'Poly ASYM Unit name', value: namePolymers.join(', ')}, + {title: 'Poly ASYM Unit chain', value: chainPolymers.join(', ')} + ]) +} +init(); \ No newline at end of file diff --git a/inspect/list_entities/tsconfig.json b/inspect/list_entities/tsconfig.json new file mode 100644 index 0000000..cbf7936 --- /dev/null +++ b/inspect/list_entities/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "strict": true, + "module": "commonjs", + "jsx": "preserve", + "esModuleInterop": true, + "sourceMap": true, + "allowJs": true, + "lib": [ + "es6", + "dom" + ], + "rootDir": "src", + "moduleResolution": "node" + } + } \ No newline at end of file diff --git a/readme.md b/readme.md index ca0937e..bf171d3 100644 --- a/readme.md +++ b/readme.md @@ -55,6 +55,8 @@ npm run watch - [Move camera](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/camera/move_camera) - Coloring - [Color a selection](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/coloring/color_a_selection) +- Inspect + - [List entities](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/inspect/list_entities) - IO - [Load file](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/io/load_file) - [Save session](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/io/save_session)