util.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. /**
  2. * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  6. */
  7. import { Structure, Model } from '../../../../mol-model/structure';
  8. import { VolumeServerInfo } from './model';
  9. import { PluginContext } from '../../../../mol-plugin/context';
  10. import { RuntimeContext } from '../../../../mol-task';
  11. import { MmcifFormat } from '../../../../mol-model-formats/structure/mmcif';
  12. export function getStreamingMethod(s?: Structure, defaultKind: VolumeServerInfo.Kind = 'x-ray'): VolumeServerInfo.Kind {
  13. if (!s) return defaultKind;
  14. const model = s.models[0];
  15. if (!MmcifFormat.is(model.sourceData)) return defaultKind;
  16. // Prefer EMDB entries over structure-factors (SF) e.g. for 'ELECTRON CRYSTALLOGRAPHY' entries
  17. // like 6AXZ or 6KJ3 for which EMDB entries are available but map calculation from SF is hard.
  18. if (Model.hasEmMap(model)) return 'em'
  19. if (Model.hasXrayMap(model)) return 'x-ray'
  20. // Fallbacks based on experimental method
  21. if (Model.isFromEm(model)) return 'em'
  22. if (Model.isFromXray(model)) return 'x-ray'
  23. return defaultKind;
  24. }
  25. /** Returns EMD ID when available, otherwise falls back to PDB ID */
  26. export function getEmIds(model: Model): string[] {
  27. const ids: string[] = []
  28. if (!MmcifFormat.is(model.sourceData)) return [ model.entryId ]
  29. const { db_id, db_name, content_type } = model.sourceData.data.db.pdbx_database_related
  30. if (!db_name.isDefined) return [ model.entryId ]
  31. for (let i = 0, il = db_name.rowCount; i < il; ++i) {
  32. if (db_name.value(i).toUpperCase() === 'EMDB' && content_type.value(i) === 'associated EM volume') {
  33. ids.push(db_id.value(i))
  34. }
  35. }
  36. return ids
  37. }
  38. export function getXrayIds(model: Model): string[] {
  39. return [ model.entryId ]
  40. }
  41. export function getIds(method: VolumeServerInfo.Kind, s?: Structure): string[] {
  42. if (!s || !s.models.length) return []
  43. const model = s.models[0]
  44. switch (method) {
  45. case 'em': return getEmIds(model)
  46. case 'x-ray': return getXrayIds(model)
  47. }
  48. }
  49. export async function getContourLevel(provider: 'wwpdb' | 'pdbe', plugin: PluginContext, taskCtx: RuntimeContext, emdbId: string) {
  50. switch (provider) {
  51. case 'wwpdb': return getContourLevelWwpdb(plugin, taskCtx, emdbId)
  52. case 'pdbe': return getContourLevelPdbe(plugin, taskCtx, emdbId)
  53. }
  54. }
  55. export async function getContourLevelWwpdb(plugin: PluginContext, taskCtx: RuntimeContext, emdbId: string) {
  56. // TODO: parametrize to a differnt URL? in plugin settings perhaps
  57. const header = await plugin.fetch({ url: `https://ftp.wwpdb.org/pub/emdb/structures/${emdbId.toUpperCase()}/header/${emdbId.toLowerCase()}.xml`, type: 'xml' }).runInContext(taskCtx);
  58. const map = header.getElementsByTagName('map')[0]
  59. const contourLevel = parseFloat(map.getElementsByTagName('contourLevel')[0].textContent!)
  60. return contourLevel;
  61. }
  62. export async function getContourLevelPdbe(plugin: PluginContext, taskCtx: RuntimeContext, emdbId: string) {
  63. emdbId = emdbId.toUpperCase()
  64. // TODO: parametrize to a differnt URL? in plugin settings perhaps
  65. const header = await plugin.fetch({ url: `https://www.ebi.ac.uk/pdbe/api/emdb/entry/map/${emdbId}`, type: 'json' }).runInContext(taskCtx);
  66. const emdbEntry = header?.[emdbId];
  67. let contourLevel: number | undefined = void 0;
  68. if (emdbEntry?.[0]?.map?.contour_level?.value !== void 0) {
  69. contourLevel = +emdbEntry[0].map.contour_level.value;
  70. }
  71. return contourLevel;
  72. }
  73. export async function getEmdbIds(plugin: PluginContext, taskCtx: RuntimeContext, pdbId: string) {
  74. // TODO: parametrize to a differnt URL? in plugin settings perhaps
  75. const summary = await plugin.fetch({ url: `https://www.ebi.ac.uk/pdbe/api/pdb/entry/summary/${pdbId}`, type: 'json' }).runInContext(taskCtx);
  76. const summaryEntry = summary?.[pdbId];
  77. let emdbIds: string[] = [];
  78. if (summaryEntry?.[0]?.related_structures) {
  79. const emdb = summaryEntry[0].related_structures.filter((s: any) => s.resource === 'EMDB' && s.relationship === 'associated EM volume');
  80. if (!emdb.length) {
  81. throw new Error(`No related EMDB entry found for '${pdbId}'.`);
  82. }
  83. emdbIds.push(...emdb.map((e: { accession: string }) => e.accession));
  84. } else {
  85. throw new Error(`No related EMDB entry found for '${pdbId}'.`);
  86. }
  87. return emdbIds
  88. }