Переглянути джерело

Proteopedia wrapper conservation coloring

David Sehnal 6 роки тому
батько
коміт
7acfe77ffa

+ 70 - 12
src/examples/proteopedia-wrapper/annotation.ts

@@ -5,26 +5,84 @@
  */
 
 import { CustomElementProperty } from 'mol-model-props/common/custom-element-property';
-import { Model, ElementIndex } from 'mol-model/structure';
+import { Model, ElementIndex, ResidueIndex } from 'mol-model/structure';
 import { Color } from 'mol-util/color';
 
-export const StripedResidues = CustomElementProperty.create<number>({
+// export const StripedResidues = CustomElementProperty.create<number>({
+//     isStatic: true,
+//     name: 'basic-wrapper-residue-striping',
+//     display: 'Residue Stripes',
+//     getData(model: Model) {
+//         const map = new Map<ElementIndex, number>();
+//         const residueIndex = model.atomicHierarchy.residueAtomSegments.index;
+//         for (let i = 0, _i = model.atomicHierarchy.atoms._rowCount; i < _i; i++) {
+//             map.set(i as ElementIndex, residueIndex[i] % 2);
+//         }
+//         return map;
+//     },
+//     coloring: {
+//         getColor(e) { return e === 0 ? Color(0xff0000) : Color(0x0000ff) },
+//         defaultColor: Color(0x777777)
+//     },
+//     format(e) {
+//         return e === 0 ? 'Odd stripe' : 'Even stripe'
+//     }
+// });
+
+const EvolutionaryConservationPalette: Color[] = [
+    [255, 255, 150], // 9
+    [160, 37, 96],
+    [240, 125, 171],
+    [250, 201, 222],
+    [252, 237, 244],
+    [255, 255, 255],
+    [234, 255, 255],
+    [215, 255, 255],
+    [140, 255, 255],
+    [16, 200, 209] // 1
+].reverse().map(([r, g, b]) => Color.fromRgb(r, g, b));
+
+export const EvolutionaryConservation = CustomElementProperty.create<number>({
     isStatic: true,
-    name: 'basic-wrapper-residue-striping',
-    display: 'Residue Stripes',
-    getData(model: Model) {
+    name: 'proteopedia-wrapper-evolutionary-conservation',
+    display: 'Evolutionary Conservation',
+    async getData(model: Model) {
+        const id = model.label.toLowerCase();
+        const req = await fetch(`https://proteopedia.org/cgi-bin/cnsrf?${id}`);
+        const json = await req.json();
+        const annotations = (json && json.residueAnnotations) || [];
+
+        const conservationMap = new Map<string, number>();
+
+        for (const e of annotations) {
+            for (const r of e.ids) {
+                conservationMap.set(r, e.annotation);
+            }
+        }
+
         const map = new Map<ElementIndex, number>();
-        const residueIndex = model.atomicHierarchy.residueAtomSegments.index;
-        for (let i = 0, _i = model.atomicHierarchy.atoms._rowCount; i < _i; i++) {
-            map.set(i as ElementIndex, residueIndex[i] % 2);
+
+        const { _rowCount: residueCount } = model.atomicHierarchy.residues;
+        const { offsets: residueOffsets } = model.atomicHierarchy.residueAtomSegments;
+        const chainIndex = model.atomicHierarchy.chainAtomSegments.index;
+
+        for (let rI = 0 as ResidueIndex; rI < residueCount; rI++) {
+            const cI = chainIndex[residueOffsets[rI]];
+            const key = `${model.atomicHierarchy.chains.auth_asym_id.value(cI)} ${model.atomicHierarchy.residues.auth_seq_id.value(rI)}`;
+            if (!conservationMap.has(key)) continue;
+            const ann = conservationMap.get(key)!;
+            for (let aI = residueOffsets[rI]; aI < residueOffsets[aI + 1]; aI++) {
+                map.set(aI, ann);
+            }
         }
+
         return map;
     },
     coloring: {
-        getColor(e) { return e === 0 ? Color(0xff0000) : Color(0x0000ff) },
-        defaultColor: Color(0x777777)
+        getColor(e) { return EvolutionaryConservationPalette[(e - 1) || 0]; },
+        defaultColor: Color(0x999999)
     },
     format(e) {
-        return e === 0 ? 'Odd stripe' : 'Even stripe'
+        return e ? `Evolutionary Conservation ${e}` : void 0;
     }
-})
+});

+ 4 - 2
src/examples/proteopedia-wrapper/index.html

@@ -61,7 +61,7 @@
 
             function $(id) { return document.getElementById(id); }
         
-            var pdbId = '3usb', assemblyId= 'preferred';
+            var pdbId = '2c1z', assemblyId= 'preferred';
             var url = 'https://www.ebi.ac.uk/pdbe/static/entry/' + pdbId + '_updated.cif';
             var format = 'cif';
             
@@ -107,9 +107,11 @@
             addControl('Play Loop', () => MolStarProteopediaWrapper.animate.modelIndex.loop());
             addControl('Stop', () => MolStarProteopediaWrapper.animate.modelIndex.stop());
 
+            addSeparator();
             addHeader('Misc');
 
-            addControl('Apply Stripes', () => MolStarProteopediaWrapper.coloring.applyStripes());
+            addControl('Apply Evo Cons', () => MolStarProteopediaWrapper.coloring.evolutionaryConservation());
+            addControl('Default Visuals', () => MolStarProteopediaWrapper.updateStyle());
 
             ////////////////////////////////////////////////////////
 

+ 15 - 15
src/examples/proteopedia-wrapper/index.ts

@@ -14,7 +14,7 @@ import { Color } from 'mol-util/color';
 import { PluginStateObject as PSO, PluginStateObject } from 'mol-plugin/state/objects';
 import { AnimateModelIndex } from 'mol-plugin/state/animation/built-in';
 import { StateBuilder, StateObject } from 'mol-state';
-import { StripedResidues } from './annotation';
+import { EvolutionaryConservation } from './annotation';
 import { LoadParams, SupportedFormats, RepresentationStyle, ModelInfo } from './helpers';
 import { RxEventHelper } from 'mol-util/rx-event-helper';
 require('mol-plugin/skin/light.scss')
@@ -39,9 +39,9 @@ class MolStarProteopediaWrapper {
             }
         });
 
-        this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(StripedResidues.Descriptor.name, StripedResidues.colorTheme!);
-        this.plugin.lociLabels.addProvider(StripedResidues.labelProvider);
-        this.plugin.customModelProperties.register(StripedResidues.propertyProvider);
+        this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(EvolutionaryConservation.Descriptor.name, EvolutionaryConservation.colorTheme!);
+        this.plugin.lociLabels.addProvider(EvolutionaryConservation.labelProvider);
+        this.plugin.customModelProperties.register(EvolutionaryConservation.propertyProvider);
     }
 
     get state() {
@@ -65,7 +65,7 @@ class MolStarProteopediaWrapper {
         const model = this.state.build().to('model');
 
         return model
-            .apply(StateTransforms.Model.CustomModelProperties, { properties: [StripedResidues.Descriptor.name] }, { ref: 'props', props: { isGhost: false } })
+            .apply(StateTransforms.Model.CustomModelProperties, { properties: [EvolutionaryConservation.Descriptor.name] }, { ref: 'props', props: { isGhost: false } })
             .apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: 'asm' });
     }
 
@@ -97,7 +97,7 @@ class MolStarProteopediaWrapper {
         return root;
     }
 
-    private getObj<T extends StateObject>(ref: string) {
+    private getObj<T extends StateObject>(ref: string): T['data'] {
         const state = this.state;
         const cell = state.select(ref)[0];
         if (!cell || !cell.obj) return void 0;
@@ -105,10 +105,10 @@ class MolStarProteopediaWrapper {
     }
 
     private async doInfo(checkPreferredAssembly: boolean) {
-        const s = this.getObj<PluginStateObject.Molecule.Model>('model');
-        if (!s) return;
+        const model = this.getObj<PluginStateObject.Molecule.Model>('model');
+        if (!model) return;
 
-        const info = await ModelInfo.get(this.plugin, s, checkPreferredAssembly)
+        const info = await ModelInfo.get(this.plugin, model, checkPreferredAssembly)
         this.events.modelInfo.next(info);
         return info;
     }
@@ -177,18 +177,18 @@ class MolStarProteopediaWrapper {
     }
 
     coloring = {
-        applyStripes: async () => {
+        evolutionaryConservation: async () => {
             await this.updateStyle({ sequence: { kind: 'molecular-surface' } });
 
             const state = this.state;
 
-            const visuals = state.selectQ(q => q.ofType(PluginStateObject.Molecule.Representation3D).filter(c => c.transform.transformer === StateTransforms.Representation.StructureRepresentation3D));
+            //const visuals = state.selectQ(q => q.ofType(PluginStateObject.Molecule.Representation3D).filter(c => c.transform.transformer === StateTransforms.Representation.StructureRepresentation3D));
             const tree = state.build();
-            const colorTheme = { name: StripedResidues.Descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(StripedResidues.Descriptor.name).defaultValues };
+            const colorTheme = { name: EvolutionaryConservation.Descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(EvolutionaryConservation.Descriptor.name).defaultValues };
 
-            for (const v of visuals) {
-                tree.to(v.transform.ref).update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
-            }
+            tree.to('sequence-visual').update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
+            //for (const v of visuals) {
+            //}
 
             await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
         }

+ 16 - 9
src/mol-model-props/common/custom-element-property.ts

@@ -26,7 +26,7 @@ namespace CustomElementProperty {
         display: string,
         attachableTo?: (model: Model) => boolean,
         getData(model: Model): Map<ElementIndex, T> | Promise<Map<ElementIndex, T>>,
-        format?(e: T): string,
+        format?(e: T): string | undefined,
         coloring?: {
             getColor: (e: T) => Color,
             defaultColor: Color
@@ -43,17 +43,24 @@ namespace CustomElementProperty {
 
         function attach(model: Model) {
             return Task.create(`Attach ${params.display}`, async () => {
-                const data = await params.getData(model);
+                try {
+                    if (model.customProperties.has(Descriptor)) return true;
 
-                if (params.isStatic) {
-                    model._staticPropertyData[name] = data;
-                } else {
-                    model._dynamicPropertyData[name] = data;
-                }
+                    const data = await params.getData(model);
+
+                    if (params.isStatic) {
+                        model._staticPropertyData[name] = data;
+                    } else {
+                        model._dynamicPropertyData[name] = data;
+                    }
 
-                model.customProperties.add(Descriptor);
+                    model.customProperties.add(Descriptor);
 
-                return true;
+                    return true;
+                } catch (e) {
+                    console.warn('Attach Property', e);
+                    return false;
+                }
             })
         }