diff --git a/src/examples/proteopedia-wrapper/annotation.ts b/src/examples/proteopedia-wrapper/annotation.ts index 54c89117c7f78c5209364fab76dcd1d35f3f7599..54596826bb42f17e08f7bf6d8aca972f8c24474b 100644 --- a/src/examples/proteopedia-wrapper/annotation.ts +++ b/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; } -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/src/examples/proteopedia-wrapper/index.html b/src/examples/proteopedia-wrapper/index.html index 59b5a762d6d7dc89cb42d34f862df3c5bc70f86f..c85de9b2d7ab92c941dcd6940c1bc2861f9c8bc1 100644 --- a/src/examples/proteopedia-wrapper/index.html +++ b/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()); //////////////////////////////////////////////////////// diff --git a/src/examples/proteopedia-wrapper/index.ts b/src/examples/proteopedia-wrapper/index.ts index 3868ddf08a495c14e553e41fe969087b7f7ff298..56f3452510fed74120bcdb43e99ae99eff9f9628 100644 --- a/src/examples/proteopedia-wrapper/index.ts +++ b/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 }); } diff --git a/src/mol-model-props/common/custom-element-property.ts b/src/mol-model-props/common/custom-element-property.ts index 609674ae6f8b420e841e2033f366c1e91fa72969..85f9306753784f838dbc00dce60b168394c5b6be 100644 --- a/src/mol-model-props/common/custom-element-property.ts +++ b/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; + } }) }