entity-source.ts 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. /**
  2. * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { StructureProperties, StructureElement, Bond, Model } from '../../mol-model/structure';
  7. import { Color } from '../../mol-util/color';
  8. import { Location } from '../../mol-model/location';
  9. import { ColorTheme, LocationColor } from '../color';
  10. import { ParamDefinition as PD } from '../../mol-util/param-definition'
  11. import { ThemeDataContext } from '../../mol-theme/theme';
  12. import { Table, Column } from '../../mol-data/db';
  13. import { mmCIF_Schema } from '../../mol-io/reader/cif/schema/mmcif';
  14. import { getPaletteParams, getPalette } from '../../mol-util/color/palette';
  15. import { TableLegend, ScaleLegend } from '../../mol-util/legend';
  16. import { isInteger } from '../../mol-util/number';
  17. import { ColorLists, getColorListFromName } from '../../mol-util/color/lists';
  18. import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
  19. const DefaultList = 'dark-2'
  20. const DefaultColor = Color(0xFAFAFA)
  21. const Description = 'Gives ranges of a polymer chain a color based on the entity source it originates from (e.g. gene, plasmid, organism).'
  22. export const EntitySourceColorThemeParams = {
  23. ...getPaletteParams({ type: 'colors', colorList: DefaultList }),
  24. }
  25. export type EntitySourceColorThemeParams = typeof EntitySourceColorThemeParams
  26. export function getEntitySourceColorThemeParams(ctx: ThemeDataContext) {
  27. const params = PD.clone(EntitySourceColorThemeParams)
  28. if (ctx.structure) {
  29. if (getMaps(ctx.structure.root.models).srcKeySerialMap.size > ColorLists[DefaultList].list.length) {
  30. params.palette.defaultValue.name = 'colors'
  31. params.palette.defaultValue.params = {
  32. ...params.palette.defaultValue.params,
  33. list: { kind: 'interpolate', colors: getColorListFromName(DefaultList).list }
  34. }
  35. }
  36. }
  37. return params
  38. }
  39. function modelEntityKey(modelIndex: number, entityId: string) {
  40. return `${modelIndex}|${entityId}`
  41. }
  42. type EntitySrc = Table<{
  43. entity_id: mmCIF_Schema['entity_src_gen']['entity_id'],
  44. pdbx_src_id: mmCIF_Schema['entity_src_gen']['pdbx_src_id'],
  45. pdbx_beg_seq_num: mmCIF_Schema['entity_src_gen']['pdbx_beg_seq_num'],
  46. pdbx_end_seq_num: mmCIF_Schema['entity_src_gen']['pdbx_end_seq_num'],
  47. }>
  48. type ScientificName = Column<mmCIF_Schema['entity_src_gen']['pdbx_gene_src_scientific_name']['T']>
  49. type GeneSrcGene = Column<mmCIF_Schema['entity_src_gen']['pdbx_gene_src_gene']['T']>
  50. type PlasmidName = Column<mmCIF_Schema['entity_src_gen']['plasmid_name']['T']>
  51. function srcKey(modelIndex: number, entityId: string, organism: string, srcId: number, plasmid: string, gene: string) {
  52. return `${modelIndex}|${entityId}|${organism}|${gene ? gene : (plasmid ? plasmid : srcId)}`
  53. }
  54. function addSrc(seqToSrcByModelEntity: Map<string, Int16Array>, srcKeySerialMap: Map<string, number>, modelIndex: number, model: Model, entity_src: EntitySrc, scientific_name: ScientificName, plasmid_name?: PlasmidName, gene_src_gene?: GeneSrcGene) {
  55. const { entity_id, pdbx_src_id, pdbx_beg_seq_num, pdbx_end_seq_num } = entity_src
  56. for (let j = 0, jl = entity_src._rowCount; j < jl; ++j) {
  57. const entityId = entity_id.value(j)
  58. const mK = modelEntityKey(modelIndex, entityId)
  59. let seqToSrc: Int16Array
  60. if (!seqToSrcByModelEntity.has(mK)) {
  61. const entityIndex = model.entities.getEntityIndex(entityId)
  62. const seq = model.sequence.sequences[entityIndex].sequence
  63. seqToSrc = new Int16Array(seq.length)
  64. seqToSrcByModelEntity.set(mK, seqToSrc)
  65. } else {
  66. seqToSrc = seqToSrcByModelEntity.get(mK)!
  67. }
  68. const plasmid = plasmid_name ? plasmid_name.value(j) : ''
  69. const gene = gene_src_gene ? gene_src_gene.value(j)[0] : ''
  70. const sK = srcKey(modelIndex, entityId, scientific_name.value(j), pdbx_src_id.value(j), plasmid, gene)
  71. // may not be given (= 0) indicating src is for the whole seq
  72. const beg = pdbx_beg_seq_num.valueKind(j) === Column.ValueKind.Present ? pdbx_beg_seq_num.value(j) : 1
  73. const end = pdbx_end_seq_num.valueKind(j) === Column.ValueKind.Present ? pdbx_end_seq_num.value(j) : seqToSrc.length
  74. let srcIndex: number // serial no starting from 1
  75. if (srcKeySerialMap.has(sK)) {
  76. srcIndex = srcKeySerialMap.get(sK)!
  77. } else {
  78. srcIndex = srcKeySerialMap.size + 1
  79. srcKeySerialMap.set(sK, srcIndex)
  80. }
  81. // set src index
  82. for (let i = beg, il = end; i <= il; ++i) {
  83. seqToSrc[i - 1] = srcIndex
  84. }
  85. }
  86. }
  87. function getMaps(models: ReadonlyArray<Model>) {
  88. const seqToSrcByModelEntity = new Map<string, Int16Array>()
  89. const srcKeySerialMap = new Map<string, number>() // serial no starting from 1
  90. for (let i = 0, il = models.length; i <il; ++i) {
  91. const m = models[i]
  92. if (!MmcifFormat.is(m.sourceData)) continue
  93. const { entity_src_gen, entity_src_nat, pdbx_entity_src_syn } = m.sourceData.data.db
  94. addSrc(seqToSrcByModelEntity, srcKeySerialMap, i, m, entity_src_gen, entity_src_gen.pdbx_gene_src_scientific_name, entity_src_gen.plasmid_name, entity_src_gen.pdbx_gene_src_gene)
  95. addSrc(seqToSrcByModelEntity, srcKeySerialMap, i, m, entity_src_nat, entity_src_nat.pdbx_organism_scientific, entity_src_nat.pdbx_plasmid_name)
  96. addSrc(seqToSrcByModelEntity, srcKeySerialMap, i, m, pdbx_entity_src_syn, pdbx_entity_src_syn.organism_scientific)
  97. }
  98. return { seqToSrcByModelEntity, srcKeySerialMap }
  99. }
  100. function getLabelTable(srcKeySerialMap: Map<string, number>) {
  101. let unnamedCount = 0
  102. return Array.from(srcKeySerialMap.keys()).map(v => {
  103. const vs = v.split('|')
  104. const organism = vs[2]
  105. const name = isInteger(vs[3]) ? `Unnamed ${++unnamedCount}` : vs[3]
  106. return `${name}${organism ? ` (${organism})` : ''}`
  107. })
  108. }
  109. export function EntitySourceColorTheme(ctx: ThemeDataContext, props: PD.Values<EntitySourceColorThemeParams>): ColorTheme<EntitySourceColorThemeParams> {
  110. let color: LocationColor
  111. let legend: ScaleLegend | TableLegend | undefined
  112. if (ctx.structure) {
  113. const l = StructureElement.Location.create(ctx.structure)
  114. const { models } = ctx.structure.root
  115. const { seqToSrcByModelEntity, srcKeySerialMap } = getMaps(models)
  116. const labelTable = getLabelTable(srcKeySerialMap)
  117. props.palette.params.valueLabel = (i: number) => labelTable[i]
  118. const palette = getPalette(srcKeySerialMap.size, props)
  119. legend = palette.legend
  120. const getSrcColor = (location: StructureElement.Location) => {
  121. const modelIndex = models.indexOf(location.unit.model)
  122. const entityId = StructureProperties.entity.id(location)
  123. const mK = modelEntityKey(modelIndex, entityId)
  124. const seqToSrc = seqToSrcByModelEntity.get(mK)
  125. if (seqToSrc) {
  126. // minus 1 to convert seqId to array index
  127. return palette.color(seqToSrc[StructureProperties.residue.label_seq_id(location) - 1] - 1)
  128. } else {
  129. return DefaultColor
  130. }
  131. }
  132. color = (location: Location): Color => {
  133. if (StructureElement.Location.is(location)) {
  134. return getSrcColor(location)
  135. } else if (Bond.isLocation(location)) {
  136. l.unit = location.aUnit
  137. l.element = location.aUnit.elements[location.aIndex]
  138. return getSrcColor(l)
  139. }
  140. return DefaultColor
  141. }
  142. } else {
  143. color = () => DefaultColor
  144. }
  145. return {
  146. factory: EntitySourceColorTheme,
  147. granularity: 'group',
  148. color,
  149. props,
  150. description: Description,
  151. legend
  152. }
  153. }
  154. export const EntitySourceColorThemeProvider: ColorTheme.Provider<EntitySourceColorThemeParams, 'entity-source'> = {
  155. name: 'entity-source',
  156. label: 'Entity Source',
  157. category: ColorTheme.Category.Chain,
  158. factory: EntitySourceColorTheme,
  159. getParams: getEntitySourceColorThemeParams,
  160. defaultValues: PD.getDefaultValues(EntitySourceColorThemeParams),
  161. isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
  162. }