Pārlūkot izejas kodu

mol-model-props: support outlier type coloring in RCSB validation theme

David Sehnal 5 gadi atpakaļ
vecāks
revīzija
fb3017cfff

+ 36 - 6
src/mol-model-props/pdbe/structure-quality-report.ts

@@ -11,7 +11,7 @@ import { mmCIF_residueId_schema } from '../../mol-io/reader/cif/schema/mmcif-ext
 import { CifWriter } from '../../mol-io/writer/cif';
 import { CifWriter } from '../../mol-io/writer/cif';
 import { Model, CustomPropertyDescriptor, ResidueIndex, Unit, IndexedCustomProperty } from '../../mol-model/structure';
 import { Model, CustomPropertyDescriptor, ResidueIndex, Unit, IndexedCustomProperty } from '../../mol-model/structure';
 import { residueIdFields } from '../../mol-model/structure/export/categories/atom_site';
 import { residueIdFields } from '../../mol-model/structure/export/categories/atom_site';
-import { StructureElement, CifExportContext } from '../../mol-model/structure/structure';
+import { StructureElement, CifExportContext, Structure } from '../../mol-model/structure/structure';
 import { CustomPropSymbol } from '../../mol-script/language/symbol';
 import { CustomPropSymbol } from '../../mol-script/language/symbol';
 import Type from '../../mol-script/language/type';
 import Type from '../../mol-script/language/type';
 import { QuerySymbolRuntime } from '../../mol-script/runtime/query/compiler';
 import { QuerySymbolRuntime } from '../../mol-script/runtime/query/compiler';
@@ -19,10 +19,14 @@ import { PropertyWrapper } from '../common/wrapper';
 import { CustomModelProperty } from '../common/custom-model-property';
 import { CustomModelProperty } from '../common/custom-model-property';
 import { ParamDefinition as PD } from '../../mol-util/param-definition'
 import { ParamDefinition as PD } from '../../mol-util/param-definition'
 import { CustomProperty } from '../common/custom-property';
 import { CustomProperty } from '../common/custom-property';
+import { arraySetAdd } from '../../mol-util/array';
 
 
 export { StructureQualityReport }
 export { StructureQualityReport }
 
 
-type StructureQualityReport = PropertyWrapper<IndexedCustomProperty.Residue<string[]> | undefined>
+type StructureQualityReport = PropertyWrapper<{
+    issues: IndexedCustomProperty.Residue<string[]>,
+    issueTypes: string[]
+}| undefined>
 
 
 namespace StructureQualityReport {
 namespace StructureQualityReport {
     export const DefaultServerUrl = 'https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry/'
     export const DefaultServerUrl = 'https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry/'
@@ -88,7 +92,14 @@ namespace StructureQualityReport {
         const prop = StructureQualityReportProvider.get(e.unit.model).value;
         const prop = StructureQualityReportProvider.get(e.unit.model).value;
         if (!prop || !prop.data) return _emptyArray;
         if (!prop || !prop.data) return _emptyArray;
         const rI = e.unit.residueIndex[e.element];
         const rI = e.unit.residueIndex[e.element];
-        return prop.data.has(rI) ? prop.data.get(rI)! : _emptyArray;
+        return prop.data.issues.has(rI) ? prop.data.issues.get(rI)! : _emptyArray;
+    }
+
+    export function getIssueTypes(structure?: Structure) {
+        if (!structure) return _emptyArray;
+        const prop = StructureQualityReportProvider.get(structure.models[0]).value;
+        if (!prop || !prop.data) return _emptyArray;
+        return prop.data.issueTypes;
     }
     }
 
 
     function getCifData(model: Model) {
     function getCifData(model: Model) {
@@ -174,7 +185,7 @@ function createExportContext(ctx: CifExportContext): ReportExportContext {
         if (prop) info = prop.info;
         if (prop) info = prop.info;
         if (!prop || !prop.data) continue;
         if (!prop || !prop.data) continue;
 
 
-        const { elements, property } = prop.data.getElements(s);
+        const { elements, property } = prop.data.issues.getElements(s);
         if (elements.length === 0) continue;
         if (elements.length === 0) continue;
 
 
         const elementGroupId: number[] = [];
         const elementGroupId: number[] = [];
@@ -205,6 +216,8 @@ function createIssueMapFromJson(modelData: Model, data: any): StructureQualityRe
     const ret = new Map<ResidueIndex, string[]>();
     const ret = new Map<ResidueIndex, string[]>();
     if (!data.molecules) return;
     if (!data.molecules) return;
 
 
+    const issueTypes: string[] = [];
+
     for (const entity of data.molecules) {
     for (const entity of data.molecules) {
         const entity_id = entity.entity_id.toString();
         const entity_id = entity.entity_id.toString();
         for (const chain of entity.chains) {
         for (const chain of entity.chains) {
@@ -217,12 +230,19 @@ function createIssueMapFromJson(modelData: Model, data: any): StructureQualityRe
                     const auth_seq_id = residue.author_residue_number, ins_code = residue.author_insertion_code || '';
                     const auth_seq_id = residue.author_residue_number, ins_code = residue.author_insertion_code || '';
                     const idx = modelData.atomicHierarchy.index.findResidue(entity_id, asym_id, auth_seq_id, ins_code);
                     const idx = modelData.atomicHierarchy.index.findResidue(entity_id, asym_id, auth_seq_id, ins_code);
                     ret.set(idx, residue.outlier_types);
                     ret.set(idx, residue.outlier_types);
+
+                    for (const t of residue.outlier_types) {
+                        arraySetAdd(issueTypes, t);
+                    }
                 }
                 }
             }
             }
         }
         }
     }
     }
 
 
-    return IndexedCustomProperty.fromResidueMap(ret);
+    return {
+        issues: IndexedCustomProperty.fromResidueMap(ret),
+        issueTypes
+    };
 }
 }
 
 
 function createIssueMapFromCif(modelData: Model,
 function createIssueMapFromCif(modelData: Model,
@@ -240,7 +260,17 @@ function createIssueMapFromCif(modelData: Model,
         ret.set(idx, groups.get(issue_type_group_id.value(i))!);
         ret.set(idx, groups.get(issue_type_group_id.value(i))!);
     }
     }
 
 
-    return IndexedCustomProperty.fromResidueMap(ret);
+    const issueTypes: string[] = [];
+    groups.forEach(issues => {
+        for (const t of issues) {
+            arraySetAdd(issueTypes, t);
+        }
+    })
+
+    return {
+        issues: IndexedCustomProperty.fromResidueMap(ret),
+        issueTypes
+    };
 }
 }
 
 
 function parseIssueTypes(groupData: Table<typeof StructureQualityReport.Schema.pdbe_structure_quality_report_issue_types>): Map<number, string[]> {
 function parseIssueTypes(groupData: Table<typeof StructureQualityReport.Schema.pdbe_structure_quality_report_issue_types>): Map<number, string[]> {

+ 59 - 6
src/mol-model-props/pdbe/themes/structure-quality-report.ts

@@ -11,6 +11,8 @@ import { ColorTheme, LocationColor } from '../../../mol-theme/color';
 import { ThemeDataContext } from '../../../mol-theme/theme';
 import { ThemeDataContext } from '../../../mol-theme/theme';
 import { Color } from '../../../mol-util/color';
 import { Color } from '../../../mol-util/color';
 import { TableLegend } from '../../../mol-util/legend';
 import { TableLegend } from '../../../mol-util/legend';
+import { ParamDefinition as PD } from '../../../mol-util/param-definition';
+import { CustomProperty } from '../../common/custom-property';
 
 
 const ValidationColors = [
 const ValidationColors = [
     Color.fromRgb(170, 170, 170), // not applicable
     Color.fromRgb(170, 170, 170), // not applicable
@@ -28,16 +30,38 @@ const ValidationColorTable: [string, Color][] = [
     ['Not Applicable', ValidationColors[9]]
     ['Not Applicable', ValidationColors[9]]
 ]
 ]
 
 
-export function StructureQualityReportColorTheme(ctx: ThemeDataContext, props: {}): ColorTheme<{}> {
+export const StructureQualityReportColorThemeParams = {
+    type: PD.MappedStatic('issue-count', {
+        'issue-count': PD.Group({}),
+        'specific-issue': PD.Group({
+            kind: PD.Text()
+        })
+    })
+};
+
+type Params = typeof StructureQualityReportColorThemeParams
+
+export function StructureQualityReportColorTheme(ctx: ThemeDataContext, props: PD.Values<Params>): ColorTheme<Params> {
     let color: LocationColor
     let color: LocationColor
 
 
     if (ctx.structure && !ctx.structure.isEmpty && ctx.structure.models[0].customProperties.has(StructureQualityReportProvider.descriptor)) {
     if (ctx.structure && !ctx.structure.isEmpty && ctx.structure.models[0].customProperties.has(StructureQualityReportProvider.descriptor)) {
         const getIssues = StructureQualityReport.getIssues;
         const getIssues = StructureQualityReport.getIssues;
-        color = (location: Location) => {
-            if (StructureElement.Location.is(location)) {
-                return ValidationColors[Math.min(3, getIssues(location).length) + 1];
+
+        if (props.type.name === 'issue-count') {
+            color = (location: Location) => {
+                if (StructureElement.Location.is(location)) {
+                    return ValidationColors[Math.min(3, getIssues(location).length) + 1];
+                }
+                return ValidationColors[0];
+            }
+        } else {
+            const issue = props.type.params.kind;
+            color = (location: Location) => {
+                if (StructureElement.Location.is(location) && getIssues(location).indexOf(issue) >= 0) {
+                    return ValidationColors[4];
+                }
+                return ValidationColors[0];
             }
             }
-            return ValidationColors[0];
         }
         }
     } else {
     } else {
         color = () => ValidationColors[0];
         color = () => ValidationColors[0];
@@ -48,7 +72,36 @@ export function StructureQualityReportColorTheme(ctx: ThemeDataContext, props: {
         granularity: 'group',
         granularity: 'group',
         color: color,
         color: color,
         props: props,
         props: props,
-        description: 'Assigns residue colors according to the number of issues in the PDBe Validation Report.',
+        description: 'Assigns residue colors according to the number of issues or a specific issue in the PDBe Validation Report.',
         legend: TableLegend(ValidationColorTable)
         legend: TableLegend(ValidationColorTable)
     }
     }
+}
+
+export const StructureQualityReportColorThemeProvider: ColorTheme.Provider<Params> =  {
+    label: 'PDBe Structure Quality Report',
+    factory: StructureQualityReportColorTheme,
+    getParams: ctx => {
+        const issueTypes = StructureQualityReport.getIssueTypes(ctx.structure);
+        if (issueTypes.length === 0) {
+            return {
+                type: PD.MappedStatic('issue-count', {
+                    'issue-count': PD.Group({})
+                })
+            };
+        }
+
+        return {
+            type: PD.MappedStatic('issue-count', {
+                'issue-count': PD.Group({}),
+                'specific-issue': PD.Group({
+                    kind: PD.Select(issueTypes[0], PD.arrayToOptions(issueTypes))
+                }, { isFlat: true })
+            })
+        };
+    },
+    defaultValues: PD.getDefaultValues(StructureQualityReportColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => StructureQualityReport.isApplicable(ctx.structure?.models[0]),
+    ensureCustomProperties: (ctx: CustomProperty.Context, data: ThemeDataContext) => {
+        return data.structure ? StructureQualityReportProvider.attach(ctx, data.structure.models[0]) : Promise.resolve()
+    }
 }
 }

+ 2 - 13
src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts

@@ -6,13 +6,11 @@
 
 
 import { OrderedSet } from '../../../../../mol-data/int';
 import { OrderedSet } from '../../../../../mol-data/int';
 import { StructureQualityReport, StructureQualityReportProvider } from '../../../../../mol-model-props/pdbe/structure-quality-report';
 import { StructureQualityReport, StructureQualityReportProvider } from '../../../../../mol-model-props/pdbe/structure-quality-report';
-import { StructureQualityReportColorTheme } from '../../../../../mol-model-props/pdbe/themes/structure-quality-report';
+import { StructureQualityReportColorThemeProvider } from '../../../../../mol-model-props/pdbe/themes/structure-quality-report';
 import { Loci } from '../../../../../mol-model/loci';
 import { Loci } from '../../../../../mol-model/loci';
 import { StructureElement } from '../../../../../mol-model/structure';
 import { StructureElement } from '../../../../../mol-model/structure';
 import { ParamDefinition as PD } from '../../../../../mol-util/param-definition';
 import { ParamDefinition as PD } from '../../../../../mol-util/param-definition';
 import { PluginBehavior } from '../../../behavior';
 import { PluginBehavior } from '../../../behavior';
-import { ThemeDataContext } from '../../../../../mol-theme/theme';
-import { CustomProperty } from '../../../../../mol-model-props/common/custom-property';
 
 
 export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: boolean, showTooltip: boolean }>({
 export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: boolean, showTooltip: boolean }>({
     name: 'pdbe-structure-quality-report-prop',
     name: 'pdbe-structure-quality-report-prop',
@@ -45,16 +43,7 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo
             this.ctx.customModelProperties.register(this.provider, false);
             this.ctx.customModelProperties.register(this.provider, false);
             this.ctx.lociLabels.addProvider(this.labelPDBeValidation);
             this.ctx.lociLabels.addProvider(this.labelPDBeValidation);
 
 
-            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add('pdbe-structure-quality-report', {
-                label: 'PDBe Structure Quality Report',
-                factory: StructureQualityReportColorTheme,
-                getParams: () => ({}),
-                defaultValues: {},
-                isApplicable: (ctx: ThemeDataContext) => StructureQualityReport.isApplicable(ctx.structure?.models[0]),
-                ensureCustomProperties: (ctx: CustomProperty.Context, data: ThemeDataContext) => {
-                    return data.structure ? StructureQualityReportProvider.attach(ctx, data.structure.models[0]) : Promise.resolve()
-                }
-            })
+            this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add('pdbe-structure-quality-report', StructureQualityReportColorThemeProvider)
         }
         }
 
 
         update(p: { autoAttach: boolean, showTooltip: boolean }) {
         update(p: { autoAttach: boolean, showTooltip: boolean }) {