diff --git a/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts b/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts index 44d2743edacb14fdba7884afe469dd500696fb44..27c9cd543282d1d76a194cbf8f6ff793e9599af3 100644 --- a/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts +++ b/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts @@ -2,6 +2,7 @@ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> + * @author Alexander Rose <alexander.rose@weirdbyte.de> */ import { PluginStateObject as SO, PluginStateTransform } from '../../../state/objects'; @@ -13,38 +14,39 @@ import { urlCombine } from '../../../../mol-util/url'; import { createIsoValueParam } from '../../../../mol-repr/volume/isosurface'; import { VolumeIsoValue } from '../../../../mol-model/volume'; import { StateAction, StateObject, StateTransformer } from '../../../../mol-state'; -import { getStreamingMethod, getEmdbIdAndContourLevel } from './util'; +import { getStreamingMethod, getId, getContourLevel, getEmdbId } from './util'; import { VolumeStreaming } from './behavior'; import { VolumeRepresentation3DHelpers } from '../../../../mol-plugin/state/transforms/representation'; import { BuiltInVolumeRepresentations } from '../../../../mol-repr/volume/registry'; import { createTheme } from '../../../../mol-theme/theme'; import { Box3D } from '../../../../mol-math/geometry'; import { Vec3 } from '../../../../mol-math/linear-algebra'; -// import { PluginContext } from '../../../../mol-plugin/context'; export const InitVolumeStreaming = StateAction.build({ display: { name: 'Volume Streaming' }, from: SO.Molecule.Structure, params(a) { const method = getStreamingMethod(a && a.data); + const id = getId(a && a.data); return { method: PD.Select<VolumeServerInfo.Kind>(method, [['em', 'EM'], ['x-ray', 'X-Ray']]), - id: PD.Text((a && a.data.models.length > 0 && a.data.models[0].entry) || ''), + id: PD.Text(id), serverUrl: PD.Text('https://ds.litemol.org'), defaultView: PD.Text<VolumeStreaming.ViewTypes>(method === 'em' ? 'cell' : 'selection-box'), - behaviorRef: PD.Text('', { isHidden: true }) + behaviorRef: PD.Text('', { isHidden: true }), + emContourProvider: PD.Select<'wwpdb' | 'pdbe'>('wwpdb', [['wwpdb', 'wwPDB'], ['pdbe', 'PDBe']], { isHidden: true }), }; }, isApplicable: (a) => a.data.models.length === 1 })(({ ref, state, params }, plugin: PluginContext) => Task.create('Volume Streaming', async taskCtx => { - // TODO: custom react view for this and the VolumeStreamingBehavior transformer - let dataId = params.id.toLowerCase(), emDefaultContourLevel: number | undefined; if (params.method === 'em') { await taskCtx.update('Getting EMDB info...'); - const emInfo = await getEmdbIdAndContourLevel(plugin, taskCtx, dataId); - dataId = emInfo.emdbId; - emDefaultContourLevel = emInfo.contour; + if (!dataId.toUpperCase().startsWith('EMD')) { + dataId = await getEmdbId(plugin, taskCtx, dataId) + } + const contourLevel = await getContourLevel(params.emContourProvider, plugin, taskCtx, dataId); + emDefaultContourLevel = contourLevel || 0; } const infoTree = state.build().to(ref) diff --git a/src/mol-plugin/behavior/dynamic/volume-streaming/util.ts b/src/mol-plugin/behavior/dynamic/volume-streaming/util.ts index 21b343e84869c3a9e3a4d8097e80ab9cd5584d5d..2e1c5aa4963a47ff49e16f360b34809e802bb144 100644 --- a/src/mol-plugin/behavior/dynamic/volume-streaming/util.ts +++ b/src/mol-plugin/behavior/dynamic/volume-streaming/util.ts @@ -2,12 +2,14 @@ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> + * @author Alexander Rose <alexander.rose@weirdbyte.de> */ import { Structure } from '../../../../mol-model/structure'; import { VolumeServerInfo } from './model'; import { PluginContext } from '../../../../mol-plugin/context'; import { RuntimeContext } from '../../../../mol-task'; +import { getXMLNodeByName, XMLDocument } from '../../../../mol-util/xml-parser'; export function getStreamingMethod(s?: Structure, defaultKind: VolumeServerInfo.Kind = 'x-ray'): VolumeServerInfo.Kind { if (!s) return defaultKind; @@ -23,7 +25,53 @@ export function getStreamingMethod(s?: Structure, defaultKind: VolumeServerInfo. return 'x-ray'; } -export async function getEmdbIdAndContourLevel(plugin: PluginContext, taskCtx: RuntimeContext, pdbId: string) { +export function getId(s?: Structure): string { + if (!s) return '' + + const model = s.models[0] + if (model.sourceData.kind !== 'mmCIF') return '' + + const d = model.sourceData.data + for (let i = 0, il = d.pdbx_database_related._rowCount; i < il; ++i) { + if (d.pdbx_database_related.db_name.value(i).toUpperCase() === 'EMDB') { + return d.pdbx_database_related.db_id.value(i) + } + } + + return s.models.length > 0 ? s.models[0].entryId : '' +} + +export async function getContourLevel(provider: 'wwpdb' | 'pdbe', plugin: PluginContext, taskCtx: RuntimeContext, emdbId: string) { + switch (provider) { + case 'wwpdb': return getContourLevelWwpdb(plugin, taskCtx, emdbId) + case 'pdbe': return getContourLevelPdbe(plugin, taskCtx, emdbId) + } +} + +export async function getContourLevelWwpdb(plugin: PluginContext, taskCtx: RuntimeContext, emdbId: string) { + // TODO: parametrize to a differnt URL? in plugin settings perhaps + const header = await plugin.fetch<XMLDocument>({ url: `https://ftp.wwpdb.org/pub/emdb/structures/${emdbId.toUpperCase()}/header/${emdbId.toLowerCase()}.xml`, type: 'xml' }).runInContext(taskCtx); + + const map = getXMLNodeByName('map', header!.root!.children!)! + const contourLevel = parseFloat(getXMLNodeByName('contourLevel', map.children!)!.content!) + + return contourLevel; +} + +export async function getContourLevelPdbe(plugin: PluginContext, taskCtx: RuntimeContext, emdbId: string) { + emdbId = emdbId.toUpperCase() + // TODO: parametrize to a differnt URL? in plugin settings perhaps + const header = await plugin.fetch({ url: `https://www.ebi.ac.uk/pdbe/api/emdb/entry/map/${emdbId}`, type: 'json' }).runInContext(taskCtx); + const emdbEntry = header && header[emdbId]; + let contourLevel: number | undefined = void 0; + if (emdbEntry && emdbEntry[0] && emdbEntry[0].map && emdbEntry[0].map.contour_level && emdbEntry[0].map.contour_level.value !== void 0) { + contourLevel = +emdbEntry[0].map.contour_level.value; + } + + return contourLevel; +} + +export async function getEmdbId(plugin: PluginContext, taskCtx: RuntimeContext, pdbId: string) { // TODO: parametrize to a differnt URL? in plugin settings perhaps const summary = await plugin.fetch({ url: `https://www.ebi.ac.uk/pdbe/api/pdb/entry/summary/${pdbId}`, type: 'json' }).runInContext(taskCtx); @@ -39,13 +87,5 @@ export async function getEmdbIdAndContourLevel(plugin: PluginContext, taskCtx: R throw new Error(`No related EMDB entry found for '${pdbId}'.`); } - // TODO: parametrize to a differnt URL? in plugin settings perhaps - const emdb = await plugin.fetch({ url: `https://www.ebi.ac.uk/pdbe/api/emdb/entry/map/${emdbId}`, type: 'json' }).runInContext(taskCtx); - const emdbEntry = emdb && emdb[emdbId]; - let contour: number | undefined = void 0; - if (emdbEntry && emdbEntry[0] && emdbEntry[0].map && emdbEntry[0].map.contour_level && emdbEntry[0].map.contour_level.value !== void 0) { - contour = +emdbEntry[0].map.contour_level.value; - } - - return { emdbId, contour }; + return emdbId } \ No newline at end of file