Ver Fonte

improve loading of ModelArchive schema structures

- includes AlphaFold mmcif structures
- add Viewer.loadAfdb
- add afdb GET param to Viewer html
- add Viewer default presets showing quality assessment (qmean & pLDDT)
- add alphafolddb option to download structure action
Alexander Rose há 3 anos atrás
pai
commit
c8c32b89c1

+ 3 - 0
src/apps/viewer/index.html

@@ -90,6 +90,9 @@
 
             var emdb = getParam('emdb', '[^&]+').trim();
             if (emdb) viewer.loadEmdb(emdb);
+
+            var afdb = getParam('afdb', '[^&]+').trim();
+            if (afdb) viewer.loadAfdb(afdb);
         </script>
         <!-- __MOLSTAR_ANALYTICS__ -->
     </body>

+ 53 - 4
src/apps/viewer/index.ts

@@ -11,13 +11,15 @@ import { DnatcoConfalPyramids } from '../../extensions/dnatco';
 import { G3DFormat, G3dProvider } from '../../extensions/g3d/format';
 import { GeometryExport } from '../../extensions/geo-export';
 import { MAQualityAssessment } from '../../extensions/model-archive/quality-assessment/behavior';
+import { QualityAssessmentPLDDTPreset, QualityAssessmentQmeanPreset } from '../../extensions/model-archive/quality-assessment/behavior';
+import { QualityAssessment } from '../../extensions/model-archive/quality-assessment/prop';
 import { Mp4Export } from '../../extensions/mp4-export';
 import { PDBeStructureQualityReport } from '../../extensions/pdbe';
 import { RCSBAssemblySymmetry, RCSBValidationReport } from '../../extensions/rcsb';
 import { DownloadStructure, PdbDownloadProvider } from '../../mol-plugin-state/actions/structure';
 import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
 import { PresetTrajectoryHierarchy } from '../../mol-plugin-state/builder/structure/hierarchy-preset';
-import { StructureRepresentationPresetProvider } from '../../mol-plugin-state/builder/structure/representation-preset';
+import { PresetStructureRepresentations, StructureRepresentationPresetProvider } from '../../mol-plugin-state/builder/structure/representation-preset';
 import { DataFormatProvider } from '../../mol-plugin-state/formats/provider';
 import { BuildInStructureFormat } from '../../mol-plugin-state/formats/structure';
 import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
@@ -34,7 +36,7 @@ import { PluginConfig } from '../../mol-plugin/config';
 import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
 import { PluginSpec } from '../../mol-plugin/spec';
 import { PluginState } from '../../mol-plugin/state';
-import { StateObjectSelector } from '../../mol-state';
+import { StateObjectRef, StateObjectSelector } from '../../mol-state';
 import { Asset } from '../../mol-util/assets';
 import { Color } from '../../mol-util/color';
 import '../../mol-util/polyfill';
@@ -151,7 +153,8 @@ export class Viewer {
                 [PluginConfig.VolumeStreaming.DefaultServer, o.volumeStreamingServer],
                 [PluginConfig.VolumeStreaming.Enabled, !o.volumeStreamingDisabled],
                 [PluginConfig.Download.DefaultPdbProvider, o.pdbProvider],
-                [PluginConfig.Download.DefaultEmdbProvider, o.emdbProvider]
+                [PluginConfig.Download.DefaultEmdbProvider, o.emdbProvider],
+                [PluginConfig.Structure.DefaultRepresentationPreset, ViewerAutoPreset.id],
             ]
         };
 
@@ -160,6 +163,8 @@ export class Viewer {
             : elementOrId;
         if (!element) throw new Error(`Could not get element with id '${elementOrId}'`);
         this.plugin = createPlugin(element, spec);
+
+        this.plugin.builders.structure.representation.registerPreset(ViewerAutoPreset);
     }
 
     setRemoteSnapshot(id: string) {
@@ -253,6 +258,22 @@ export class Viewer {
         }));
     }
 
+    loadAfdb(afdb: string) {
+        const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
+        return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
+            source: {
+                name: 'alphafolddb' as const,
+                params: {
+                    id: afdb,
+                    options: {
+                        ...params.source.params.options,
+                        representation: 'preset-structure-representation-ma-quality-assessment-plddt'
+                    },
+                }
+            }
+        }));
+    }
+
     /**
      * @example Load X-ray density from volume server
         viewer.loadVolumeFromUrl({
@@ -411,4 +432,32 @@ export interface LoadTrajectoryParams {
     | { kind: 'coordinates-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuildInStructureFormat },
     coordinatesLabel?: string,
     preset?: keyof PresetTrajectoryHierarchy
-}
+}
+
+export const ViewerAutoPreset = StructureRepresentationPresetProvider({
+    id: 'preset-structure-representation-viewer-auto',
+    display: {
+        name: 'Automatic (w/ Annotation)', group: 'Annotation',
+        description: 'Show standard automatic representation but colored by quality assessment (if available in the model).'
+    },
+    isApplicable(a) {
+        return (
+            !!a.data.models.some(m => QualityAssessment.isApplicable(m, 'pLDDT')) ||
+            !!a.data.models.some(m => QualityAssessment.isApplicable(m, 'qmean'))
+        );
+    },
+    params: () => StructureRepresentationPresetProvider.CommonParams,
+    async apply(ref, params, plugin) {
+        const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
+        const structure = structureCell?.obj?.data;
+        if (!structureCell || !structure) return {};
+
+        if (!!structure.models.some(m => QualityAssessment.isApplicable(m, 'pLDDT'))) {
+            return await QualityAssessmentPLDDTPreset.apply(ref, params, plugin);
+        } else if (!!structure.models.some(m => QualityAssessment.isApplicable(m, 'qmean'))) {
+            return await QualityAssessmentQmeanPreset.apply(ref, params, plugin);
+        } else {
+            return await PresetStructureRepresentations.auto.apply(ref, params, plugin);
+        }
+    }
+});

+ 16 - 2
src/mol-plugin-state/actions/structure.ts

@@ -18,6 +18,7 @@ import { Download } from '../transforms/data';
 import { CustomModelProperties, CustomStructureProperties, TrajectoryFromModelAndCoordinates } from '../transforms/model';
 import { Asset } from '../../mol-util/assets';
 import { PluginConfig } from '../../mol-plugin/config';
+import { getFileInfo } from '../../mol-util/file-info';
 
 const DownloadModelRepresentationOptions = (plugin: PluginContext) => {
     const representationDefault = plugin.config.get(PluginConfig.Structure.DefaultRepresentationPreset) || PresetStructureRepresentations.auto.id;
@@ -69,6 +70,10 @@ const DownloadStructure = StateAction.build({
                     id: PD.Text('Q9Y2I8', { label: 'UniProtKB AC(s)', description: 'One or more comma/space separated ACs.' }),
                     options
                 }, { isFlat: true, label: 'SWISS-MODEL', description: 'Loads the best homology model or experimental structure' }),
+                'alphafolddb': PD.Group({
+                    id: PD.Text('Q8W3K0', { label: 'UniProtKB AC(s)', description: 'One or more comma/space separated ACs.' }),
+                    options
+                }, { isFlat: true, label: 'AlphaFold DB', description: 'Loads the predicted model if available' }),
                 'pubchem': PD.Group({
                     id: PD.Text('2244,2245', { label: 'PubChem ID', description: 'One or more comma/space separated IDs.' }),
                     options
@@ -87,7 +92,7 @@ const DownloadStructure = StateAction.build({
 
     const src = params.source;
     let downloadParams: StateTransformer.Params<Download>[];
-    let asTrajectory = false, format: BuiltInTrajectoryFormat = 'mmcif';
+    let asTrajectory = false, format: BuiltInTrajectoryFormat | 'auto' = 'mmcif';
 
     switch (src.name) {
         case 'url':
@@ -124,6 +129,11 @@ const DownloadStructure = StateAction.build({
             asTrajectory = !!src.params.options.asTrajectory;
             format = 'pdb';
             break;
+        case 'alphafolddb':
+            downloadParams = getDownloadParams(src.params.id, id => `https://alphafold.ebi.ac.uk/files/AF-${id.toUpperCase()}-F1-model_v1.cif`, id => `AlphaFold DB: ${id}`, false);
+            asTrajectory = !!src.params.options.asTrajectory;
+            format = 'mmcif';
+            break;
         case 'pubchem':
             downloadParams = getDownloadParams(src.params.id, id => `https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/CID/${id.trim()}/record/SDF/?record_type=3d`, id => `PubChem: ${id}`, false);
             asTrajectory = !!src.params.options.asTrajectory;
@@ -154,7 +164,11 @@ const DownloadStructure = StateAction.build({
         } else {
             for (const download of downloadParams) {
                 const data = await plugin.builders.data.download(download, { state: { isGhost: true } });
-                const trajectory = await plugin.builders.structure.parseTrajectory(data, format);
+                const provider = format === 'auto'
+                    ? plugin.dataFormats.auto(getFileInfo(Asset.getUrl(download.url)), data.cell?.obj!)
+                    : plugin.dataFormats.get(format);
+                if (!provider) throw new Error('unknown file format');
+                const trajectory = await plugin.builders.structure.parseTrajectory(data, provider);
 
                 await plugin.builders.structure.hierarchy.applyPreset(trajectory, 'default', {
                     structure,