Skip to content
Snippets Groups Projects
Commit ee89a9a2 authored by David Sehnal's avatar David Sehnal
Browse files

pdbe validation color theme

parent 64276543
No related branches found
No related tags found
No related merge requests found
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { StructureQualityReport } from 'mol-model-props/pdbe/structure-quality-report';
import { Location } from 'mol-model/location';
import { StructureElement } from 'mol-model/structure';
import { ColorTheme, LocationColor } from 'mol-theme/color';
import { ThemeDataContext } from 'mol-theme/theme';
import { Color } from 'mol-util/color';
import { TableLegend } from 'mol-util/color/tables';
const ValidationColors = [
Color.fromRgb(170, 170, 170), // not applicable
Color.fromRgb(0, 255, 0), // 0 issues
Color.fromRgb(255, 255, 0), // 1
Color.fromRgb(255, 128, 0), // 2
Color.fromRgb(255, 0, 0), // 3 or more
]
const ValidationColorTable: [string, Color][] = [
['No Issues', ValidationColors[1]],
['One Issue', ValidationColors[2]],
['Two Issues', ValidationColors[3]],
['Three Or More Issues', ValidationColors[4]],
['Not Applicable', ValidationColors[9]]
]
export function StructureQualityReportColorTheme(ctx: ThemeDataContext, props: {}): ColorTheme<{}> {
let color: LocationColor
if (ctx.structure && ctx.structure.models[0].customProperties.has(StructureQualityReport.Descriptor)) {
const getIssues = StructureQualityReport.getIssues;
color = (location: Location) => {
if (StructureElement.isLocation(location)) {
return ValidationColors[Math.min(3, getIssues(location).length) + 1];
}
return ValidationColors[0];
}
} else {
color = () => ValidationColors[0];
}
return {
factory: StructureQualityReportColorTheme,
granularity: 'group',
color: color,
props: props,
description: 'Assigns residue colors according to the number of issues in the PDBe Validation Report.',
legend: TableLegend(ValidationColorTable)
}
}
\ No newline at end of file
...@@ -4,15 +4,14 @@ ...@@ -4,15 +4,14 @@
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
*/ */
import { ParamDefinition } from 'mol-util/param-definition'; import { OrderedSet } from 'mol-data/int';
import { PluginBehavior } from '../behavior';
import { StructureQualityReport } from 'mol-model-props/pdbe/structure-quality-report'; import { StructureQualityReport } from 'mol-model-props/pdbe/structure-quality-report';
import { CustomPropertyRegistry } from 'mol-plugin/util/custom-prop-registry'; import { StructureQualityReportColorTheme } 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 { OrderedSet } from 'mol-data/int'; import { CustomPropertyRegistry } from 'mol-plugin/util/custom-prop-registry';
import { ParamDefinition as PD } from 'mol-util/param-definition';
// TODO: make auto attach working better for "initial state" by supporting default props in state updates import { PluginBehavior } from '../behavior';
export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: boolean }>({ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: boolean }>({
name: 'pdbe-structure-quality-report-prop', name: 'pdbe-structure-quality-report-prop',
...@@ -33,6 +32,15 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo ...@@ -33,6 +32,15 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo
register(): void { register(): void {
this.ctx.customModelProperties.register(this.provider); this.ctx.customModelProperties.register(this.provider);
this.ctx.lociLabels.addProvider(labelPDBeValidation); this.ctx.lociLabels.addProvider(labelPDBeValidation);
// TODO: support filtering of themes based on the input structure
// in this case, it would check structure.models[0].customProperties.has(StructureQualityReport.Descriptor)
// TODO: add remove functionality
this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add('pdbe-structure-quality-report', {
label: 'PDBe Structure Quality Report',
factory: StructureQualityReportColorTheme,
getParams: () => ({})
})
} }
update(p: { autoAttach: boolean }) { update(p: { autoAttach: boolean }) {
...@@ -45,10 +53,13 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo ...@@ -45,10 +53,13 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo
unregister() { unregister() {
this.ctx.customModelProperties.unregister(StructureQualityReport.Descriptor.name); this.ctx.customModelProperties.unregister(StructureQualityReport.Descriptor.name);
this.ctx.lociLabels.removeProvider(labelPDBeValidation); this.ctx.lociLabels.removeProvider(labelPDBeValidation);
// TODO: add remove functionality to registry
// this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove('pdbe-structure-quality-report')
} }
}, },
params: () => ({ params: () => ({
autoAttach: ParamDefinition.Boolean(false) autoAttach: PD.Boolean(false)
}), }),
display: { name: 'Focus Loci on Select', group: 'Camera' } display: { name: 'Focus Loci on Select', group: 'Camera' }
}); });
......
...@@ -27,10 +27,23 @@ const DownloadStructure = StateAction.build({ ...@@ -27,10 +27,23 @@ const DownloadStructure = StateAction.build({
display: { name: 'Download Structure', description: 'Load a structure from the provided source and create its default Assembly and visual.' }, display: { name: 'Download Structure', description: 'Load a structure from the provided source and create its default Assembly and visual.' },
params: { params: {
source: PD.MappedStatic('bcif-static', { source: PD.MappedStatic('bcif-static', {
'pdbe-updated': PD.Text('1cbs', { label: 'Id' }), 'pdbe-updated': PD.Group({
'rcsb': PD.Text('1tqn', { label: 'Id' }), id: PD.Text('1cbs', { label: 'Id' }),
'bcif-static': PD.Text('1tqn', { label: 'Id' }), supportProps: PD.Boolean(false)
'url': PD.Group({ url: PD.Text(''), isBinary: PD.Boolean(false) }, { isExpanded: true }) }, { isExpanded: true }),
'rcsb': PD.Group({
id: PD.Text('1tqn', { label: 'Id' }),
supportProps: PD.Boolean(false)
}, { isExpanded: true }),
'bcif-static': PD.Group({
id: PD.Text('1tqn', { label: 'Id' }),
supportProps: PD.Boolean(false)
}, { isExpanded: true }),
'url': PD.Group({
url: PD.Text(''),
isBinary: PD.Boolean(false),
supportProps: PD.Boolean(false)
}, { isExpanded: true })
}, { }, {
options: [ options: [
['pdbe-updated', 'PDBe Updated'], ['pdbe-updated', 'PDBe Updated'],
...@@ -50,19 +63,19 @@ const DownloadStructure = StateAction.build({ ...@@ -50,19 +63,19 @@ const DownloadStructure = StateAction.build({
url = src.params; url = src.params;
break; break;
case 'pdbe-updated': case 'pdbe-updated':
url = { url: `https://www.ebi.ac.uk/pdbe/static/entry/${src.params.toLowerCase()}_updated.cif`, isBinary: false, label: `PDBe: ${src.params}` }; url = { url: `https://www.ebi.ac.uk/pdbe/static/entry/${src.params.id.toLowerCase()}_updated.cif`, isBinary: false, label: `PDBe: ${src.params}` };
break; break;
case 'rcsb': case 'rcsb':
url = { url: `https://files.rcsb.org/download/${src.params.toUpperCase()}.cif`, isBinary: false, label: `RCSB: ${src.params}` }; url = { url: `https://files.rcsb.org/download/${src.params.id.toUpperCase()}.cif`, isBinary: false, label: `RCSB: ${src.params}` };
break; break;
case 'bcif-static': case 'bcif-static':
url = { url: `https://webchem.ncbr.muni.cz/ModelServer/static/bcif/${src.params.toLowerCase()}`, isBinary: true, label: `BinaryCIF: ${src.params}` }; url = { url: `https://webchem.ncbr.muni.cz/ModelServer/static/bcif/${src.params.id.toLowerCase()}`, isBinary: true, label: `BinaryCIF: ${src.params}` };
break; break;
default: throw new Error(`${(src as any).name} not supported.`); default: throw new Error(`${(src as any).name} not supported.`);
} }
const data = b.toRoot().apply(StateTransforms.Data.Download, url); const data = b.toRoot().apply(StateTransforms.Data.Download, url);
return state.update(createStructureTree(data)); return state.update(createStructureTree(data, params.source.params.supportProps));
}); });
export const OpenStructure = StateAction.build({ export const OpenStructure = StateAction.build({
...@@ -72,16 +85,20 @@ export const OpenStructure = StateAction.build({ ...@@ -72,16 +85,20 @@ export const OpenStructure = StateAction.build({
})(({ params, state }) => { })(({ params, state }) => {
const b = state.build(); const b = state.build();
const data = b.toRoot().apply(StateTransforms.Data.ReadFile, { file: params.file, isBinary: /\.bcif$/i.test(params.file.name) }); const data = b.toRoot().apply(StateTransforms.Data.ReadFile, { file: params.file, isBinary: /\.bcif$/i.test(params.file.name) });
return state.update(createStructureTree(data)); return state.update(createStructureTree(data, false));
}); });
function createStructureTree(b: StateTreeBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>): StateTree { function createStructureTree(b: StateTreeBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, supportProps: boolean): StateTree {
const root = b let root = b
.apply(StateTransforms.Data.ParseCif) .apply(StateTransforms.Data.ParseCif)
.apply(StateTransforms.Model.TrajectoryFromMmCif, {}) .apply(StateTransforms.Model.TrajectoryFromMmCif, {})
.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 }) .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 });
.apply(StateTransforms.Model.CustomModelProperties, { properties: [] })
.apply(StateTransforms.Model.StructureAssemblyFromModel); if (supportProps) {
// TODO: implement automatic default property assigment in State.update
root = root.apply(StateTransforms.Model.CustomModelProperties, { properties: [] });
}
root = root.apply(StateTransforms.Model.StructureAssemblyFromModel);
complexRepresentation(root); complexRepresentation(root);
......
...@@ -180,7 +180,7 @@ const CustomModelProperties = PluginStateTransform.BuiltIn({ ...@@ -180,7 +180,7 @@ const CustomModelProperties = PluginStateTransform.BuiltIn({
apply({ a, params }, ctx: PluginContext) { apply({ a, params }, ctx: PluginContext) {
return Task.create('Custom Props', async taskCtx => { return Task.create('Custom Props', async taskCtx => {
await attachProps(a.data, ctx, taskCtx, params.properties); await attachProps(a.data, ctx, taskCtx, params.properties);
return new SO.Molecule.Model(a.data, { label: a.label, description: 'Custom Props' }); return new SO.Molecule.Model(a.data, { label: 'Props', description: `${params.properties.length} Selected` });
}); });
} }
}); });
......
...@@ -45,7 +45,7 @@ export class LociLabelControl extends PluginComponent<{}, { entries: ReadonlyArr ...@@ -45,7 +45,7 @@ export class LociLabelControl extends PluginComponent<{}, { entries: ReadonlyArr
} }
render() { render() {
return <div> return <div style={{ textAlign: 'right' }}>
{this.state.entries.map((e, i) => <div key={'' + i}>{e}</div>)} {this.state.entries.map((e, i) => <div key={'' + i}>{e}</div>)}
</div> </div>
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment