jbrowse-plugin-protein3d 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/AddHighlightModel/ProteinToMsaHoverSync.js +8 -4
- package/dist/AddHighlightModel/findConnectedMsaView.d.ts +23 -0
- package/dist/AddHighlightModel/findConnectedMsaView.js +23 -0
- package/dist/LaunchProteinView/components/AlphaFoldDBSearch.js +3 -3
- package/dist/LaunchProteinView/components/AlphaFoldDBSearchStatus.d.ts +2 -4
- package/dist/LaunchProteinView/components/IdentifierSelector.js +2 -36
- package/dist/LaunchProteinView/components/IsoformSequencesToggle.d.ts +2 -5
- package/dist/LaunchProteinView/components/MSATable.d.ts +2 -5
- package/dist/LaunchProteinView/components/TabPanel.js +4 -2
- package/dist/LaunchProteinView/components/TranscriptSelector.d.ts +2 -4
- package/dist/LaunchProteinView/components/TranscriptSelector.js +16 -32
- package/dist/LaunchProteinView/hooks/useAlphaFoldDBSearch.d.ts +2 -8
- package/dist/LaunchProteinView/hooks/useIsoformProteinSequences.d.ts +2 -4
- package/dist/LaunchProteinView/hooks/useTranscriptIsoformSelection.d.ts +2 -8
- package/dist/LaunchProteinView/hooks/useTranscriptSelection.d.ts +2 -4
- package/dist/LaunchProteinView/hooks/useTranscriptSelection.js +3 -3
- package/dist/LaunchProteinView/services/lookupMethods.js +2 -20
- package/dist/LaunchProteinView/utils/launchViewUtils.js +8 -6
- package/dist/LaunchProteinView/utils/util.d.ts +24 -13
- package/dist/LaunchProteinView/utils/util.js +60 -49
- package/dist/Protein1DViewRegistry/index.js +4 -9
- package/dist/ProteinView/__fixtures__/structureFixtures.d.ts +7 -0
- package/dist/ProteinView/__fixtures__/structureFixtures.js +16 -0
- package/dist/ProteinView/applyLociInteractivity.d.ts +4 -1
- package/dist/ProteinView/applyLociInteractivity.js +9 -1
- package/dist/ProteinView/chooseMappedEntity.d.ts +32 -0
- package/dist/ProteinView/chooseMappedEntity.js +71 -0
- package/dist/ProteinView/components/FeatureBar.js +1 -0
- package/dist/ProteinView/extractStructureSequences.d.ts +12 -0
- package/dist/ProteinView/extractStructureSequences.js +9 -1
- package/dist/ProteinView/loadStructureData.d.ts +2 -1
- package/dist/ProteinView/loadStructureData.js +4 -4
- package/dist/ProteinView/model.d.ts +33 -9
- package/dist/ProteinView/proteinToGenomeMapping.d.ts +1 -1
- package/dist/ProteinView/proteinToGenomeMapping.js +10 -7
- package/dist/ProteinView/structureModel.d.ts +40 -2
- package/dist/ProteinView/structureModel.js +87 -33
- package/dist/ProteinView/subscribeMolstarInteraction.d.ts +3 -0
- package/dist/ProteinView/subscribeMolstarInteraction.js +1 -0
- package/dist/jbrowse-plugin-protein3d.umd.production.min.js +16 -16
- package/dist/jbrowse-plugin-protein3d.umd.production.min.js.map +4 -4
- package/dist/mappings.d.ts +1 -0
- package/dist/mappings.js +14 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -1
- package/src/AddHighlightModel/ProteinToMsaHoverSync.tsx +9 -4
- package/src/AddHighlightModel/findConnectedMsaView.test.ts +53 -0
- package/src/AddHighlightModel/findConnectedMsaView.ts +35 -0
- package/src/LaunchProteinView/components/AlphaFoldDBSearch.tsx +5 -5
- package/src/LaunchProteinView/components/AlphaFoldDBSearchStatus.tsx +2 -1
- package/src/LaunchProteinView/components/IdentifierSelector.tsx +2 -37
- package/src/LaunchProteinView/components/IsoformSequencesToggle.tsx +2 -2
- package/src/LaunchProteinView/components/MSATable.tsx +2 -2
- package/src/LaunchProteinView/components/TabPanel.tsx +4 -2
- package/src/LaunchProteinView/components/TranscriptSelector.tsx +15 -33
- package/src/LaunchProteinView/hooks/useIsoformProteinSequences.ts +2 -3
- package/src/LaunchProteinView/hooks/useTranscriptSelection.ts +11 -13
- package/src/LaunchProteinView/services/lookupMethods.ts +2 -21
- package/src/LaunchProteinView/utils/launchViewUtils.ts +8 -6
- package/src/LaunchProteinView/utils/util.ts +98 -64
- package/src/Protein1DViewRegistry/index.test.ts +55 -0
- package/src/Protein1DViewRegistry/index.ts +5 -11
- package/src/ProteinView/__fixtures__/structureFixtures.ts +29 -0
- package/src/ProteinView/applyLociInteractivity.ts +12 -0
- package/src/ProteinView/chooseMappedEntity.test.ts +65 -0
- package/src/ProteinView/chooseMappedEntity.ts +97 -0
- package/src/ProteinView/components/FeatureBar.tsx +1 -0
- package/src/ProteinView/extractStructureSequences.ts +20 -3
- package/src/ProteinView/geneExplorerLinkage.test.ts +214 -0
- package/src/ProteinView/loadStructureData.ts +6 -5
- package/src/ProteinView/proteinToGenomeMapping.ts +12 -9
- package/src/ProteinView/structureLoader.test.ts +12 -9
- package/src/ProteinView/structureModel.ts +103 -38
- package/src/ProteinView/subscribeMolstarInteraction.ts +4 -0
- package/src/mappings.ts +18 -1
- package/src/version.ts +1 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { SimpleFeature } from '@jbrowse/core/util';
|
|
2
|
-
import { getCodonRange } from 'g2p_mapper';
|
|
3
2
|
import { action, computed, makeObservable, observable } from 'mobx';
|
|
4
|
-
import { genomeToTranscriptSeqMapping } from '../mappings';
|
|
3
|
+
import { codonGenomeSpan, genomeToTranscriptSeqMapping } from '../mappings';
|
|
5
4
|
class Protein1DViewRegistry {
|
|
6
5
|
views = observable.map();
|
|
7
6
|
constructor() {
|
|
@@ -53,13 +52,9 @@ class Protein1DViewRegistry {
|
|
|
53
52
|
if (!mapping) {
|
|
54
53
|
return undefined;
|
|
55
54
|
}
|
|
56
|
-
const {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
return undefined;
|
|
60
|
-
}
|
|
61
|
-
const [start, end] = result;
|
|
62
|
-
return { refName, start, end };
|
|
55
|
+
const { p2gCodon, refName } = mapping;
|
|
56
|
+
const span = codonGenomeSpan(p2gCodon, proteinPos);
|
|
57
|
+
return span ? { refName, start: span[0], end: span[1] } : undefined;
|
|
63
58
|
}
|
|
64
59
|
}
|
|
65
60
|
export const protein1DViewRegistry = new Protein1DViewRegistry();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const HBA_ALPHA_4HHB_ENTITY0 = "VLSPADKTNVKAAWGKVGAHAGEYGAEALERMFLSFPTTKTYFPHFDLSHGSAQVKGHGKKVADALTNAVAHVDDMPNALSALSDLHAHKLRVDPVNFKLLSHCLLVTLAAHLPAEFTPAVHASLDKFLASVSTVLTSKYR";
|
|
2
|
+
export declare const HBB_BETA_4HHB_ENTITY1 = "VHLTPEEKSAVTALWGKVNVDEVGGEALGRLLVVYPWTQRFFESFGDLSTPDAVMGNPKVKAHGKKVLGAFSDGLAHLDNLKGTFATLSELHCDKLHVDPENFRLLGNVLVCVLAHHFGKEFTPPVQAAYQKVVAGVANALAHKYH";
|
|
3
|
+
export declare const HBA_TRANSCRIPT_P69905 = "MVLSPADKTNVKAAWGKVGAHAGEYGAEALERMFLSFPTTKTYFPHFDLSHGSAQVKGHGKKVADALTNAVAHVDDMPNALSALSDLHAHKLRVDPVNFKLLSHCLLVTLAAHLPAEFTPAVHASLDKFLASVSTVLTSKYR";
|
|
4
|
+
export declare const HBB_TRANSCRIPT_P68871 = "MVHLTPEEKSAVTALWGKVNVDEVGGEALGRLLVVYPWTQRFFESFGDLSTPDAVMGNPKVKAHGKKVLGAFSDGLAHLDNLKGTFATLSELHCDKLHVDPENFRLLGNVLVCVLAHHFGKEFTPPVQAAYQKVVAGVANALAHKYH";
|
|
5
|
+
export declare const DNA_1TUP_ENTITY0 = "TTTCCTAGACTTGCCCAATTA";
|
|
6
|
+
export declare const P53_1TUP_ENTITY2 = "SSSVPSQKTYQGSYGFRLGFLHSGTAKSVTCTYSPALNKMFCQLAKTCPVQLWVDSTPPPGTRVRAMAIYKQSQHMTEVVRRCPHHERCSDSDGLAPPQHLIRVEGNLRVEYLDDRNTFRHSVVVPYEPPEVGSDCTTIHYNYMCNSSCMGGMNRRPILTIITLEDSSGNLLGRNSFEVRVCACPGRDRRTEEENLRKKGEPHHELPPGSTKRALPNNT";
|
|
7
|
+
export declare const P53_TRANSCRIPT_P04637 = "MEEPQSDPSVEPPLSQETFSDLWKLLPENNVLSPLPSQAMDDLMLSPDDIEQWFTEDPGPDEAPRMPEAAPPVAPAPAAPTPAAPAPAPSWPLSSSVPSQKTYQGSYGFRLGFLHSGTAKSVTCTYSPALNKMFCQLAKTCPVQLWVDSTPPPGTRVRAMAIYKQSQHMTEVVRRCPHHERCSDSDGLAPPQHLIRVEGNLRVEYLDDRNTFRHSVVVPYEPPEVGSDCTTIHYNYMCNSSCMGGMNRRPILTIITLEDSSGNLLGRNSFEVRVCACPGRDRRTEEENLRKKGEPHHELPPGSTKRALPNNTSSSPQPKKKPLDGEYFTLQIRGRERFEMFRELNEALELKDAQAGKEPGGSRAHSSHLKSKKGQSTSRHKKLMFKTEGPDSD";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Real entity sequences (RCSB entity_poly one-letter, canonical) and transcript
|
|
2
|
+
// sequences (UniProt canonical) for structures the entity-[0] assumption maps
|
|
3
|
+
// wrong. Frozen here so the tests are deterministic and offline.
|
|
4
|
+
//
|
|
5
|
+
// 4HHB: hemoglobin α/β tetramer — entity[0] is the α chain, so a β (HBB)
|
|
6
|
+
// transcript must NOT map to [0]. (PDB strips the initiator Met, hence the
|
|
7
|
+
// leading-M difference vs UniProt.)
|
|
8
|
+
// 1TUP: p53 core bound to DNA — entity[0] and [1] are DNA strands; the protein
|
|
9
|
+
// is entity[2].
|
|
10
|
+
export const HBA_ALPHA_4HHB_ENTITY0 = 'VLSPADKTNVKAAWGKVGAHAGEYGAEALERMFLSFPTTKTYFPHFDLSHGSAQVKGHGKKVADALTNAVAHVDDMPNALSALSDLHAHKLRVDPVNFKLLSHCLLVTLAAHLPAEFTPAVHASLDKFLASVSTVLTSKYR';
|
|
11
|
+
export const HBB_BETA_4HHB_ENTITY1 = 'VHLTPEEKSAVTALWGKVNVDEVGGEALGRLLVVYPWTQRFFESFGDLSTPDAVMGNPKVKAHGKKVLGAFSDGLAHLDNLKGTFATLSELHCDKLHVDPENFRLLGNVLVCVLAHHFGKEFTPPVQAAYQKVVAGVANALAHKYH';
|
|
12
|
+
export const HBA_TRANSCRIPT_P69905 = 'MVLSPADKTNVKAAWGKVGAHAGEYGAEALERMFLSFPTTKTYFPHFDLSHGSAQVKGHGKKVADALTNAVAHVDDMPNALSALSDLHAHKLRVDPVNFKLLSHCLLVTLAAHLPAEFTPAVHASLDKFLASVSTVLTSKYR';
|
|
13
|
+
export const HBB_TRANSCRIPT_P68871 = 'MVHLTPEEKSAVTALWGKVNVDEVGGEALGRLLVVYPWTQRFFESFGDLSTPDAVMGNPKVKAHGKKVLGAFSDGLAHLDNLKGTFATLSELHCDKLHVDPENFRLLGNVLVCVLAHHFGKEFTPPVQAAYQKVVAGVANALAHKYH';
|
|
14
|
+
export const DNA_1TUP_ENTITY0 = 'TTTCCTAGACTTGCCCAATTA';
|
|
15
|
+
export const P53_1TUP_ENTITY2 = 'SSSVPSQKTYQGSYGFRLGFLHSGTAKSVTCTYSPALNKMFCQLAKTCPVQLWVDSTPPPGTRVRAMAIYKQSQHMTEVVRRCPHHERCSDSDGLAPPQHLIRVEGNLRVEYLDDRNTFRHSVVVPYEPPEVGSDCTTIHYNYMCNSSCMGGMNRRPILTIITLEDSSGNLLGRNSFEVRVCACPGRDRRTEEENLRKKGEPHHELPPGSTKRALPNNT';
|
|
16
|
+
export const P53_TRANSCRIPT_P04637 = 'MEEPQSDPSVEPPLSQETFSDLWKLLPENNVLSPLPSQAMDDLMLSPDDIEQWFTEDPGPDEAPRMPEAAPPVAPAPAAPTPAAPAPAPSWPLSSSVPSQKTYQGSYGFRLGFLHSGTAKSVTCTYSPALNKMFCQLAKTCPVQLWVDSTPPPGTRVRAMAIYKQSQHMTEVVRRCPHHERCSDSDGLAPPQHLIRVEGNLRVEYLDDRNTFRHSVVVPYEPPEVGSDCTTIHYNYMCNSSCMGGMNRRPILTIITLEDSSGNLLGRNSFEVRVCACPGRDRRTEEENLRKKGEPHHELPPGSTKRALPNNTSSSPQPKKKPLDGEYFTLQIRGRERFEMFRELNEALELKDAQAGKEPGGSRAHSSHLKSKKGQSTSRHKKLMFKTEGPDSD';
|
|
@@ -21,9 +21,12 @@ export type ResidueSpec = {
|
|
|
21
21
|
* channel, so callers describe the target state declaratively rather than
|
|
22
22
|
* juggling clear/apply calls.
|
|
23
23
|
*/
|
|
24
|
-
export declare function setMolstarLoci({ structure, plugin, channel, spec, }: {
|
|
24
|
+
export declare function setMolstarLoci({ structure, plugin, channel, spec, entityId, }: {
|
|
25
25
|
structure: Structure;
|
|
26
26
|
plugin: PluginContext;
|
|
27
27
|
channel: 'highlight' | 'select';
|
|
28
28
|
spec: ResidueSpec | undefined;
|
|
29
|
+
/** Confine the loci to this mmCIF entity so a residue number doesn't light up
|
|
30
|
+
* on unrelated chains (binding partners, the other half of a homodimer). */
|
|
31
|
+
entityId?: string;
|
|
29
32
|
}): Promise<void>;
|
|
@@ -14,7 +14,7 @@ const isActive = (spec) => spec !== undefined &&
|
|
|
14
14
|
* channel, so callers describe the target state declaratively rather than
|
|
15
15
|
* juggling clear/apply calls.
|
|
16
16
|
*/
|
|
17
|
-
export async function setMolstarLoci({ structure, plugin, channel, spec, }) {
|
|
17
|
+
export async function setMolstarLoci({ structure, plugin, channel, spec, entityId, }) {
|
|
18
18
|
const { lociHighlights, lociSelects } = plugin.managers.interactivity;
|
|
19
19
|
if (channel === 'highlight') {
|
|
20
20
|
lociHighlights.clearHighlights();
|
|
@@ -25,6 +25,14 @@ export async function setMolstarLoci({ structure, plugin, channel, spec, }) {
|
|
|
25
25
|
if (isActive(spec)) {
|
|
26
26
|
const { StructureSelection, Script } = await loadMolstar();
|
|
27
27
|
const sel = Script.getStructureSelection(Q => Q.struct.generator.atomGroups({
|
|
28
|
+
...(entityId
|
|
29
|
+
? {
|
|
30
|
+
'chain-test': Q.core.rel.eq([
|
|
31
|
+
Q.struct.atomProperty.macromolecular.label_entity_id(),
|
|
32
|
+
entityId,
|
|
33
|
+
]),
|
|
34
|
+
}
|
|
35
|
+
: {}),
|
|
28
36
|
'residue-test': specToTest(spec)(Q),
|
|
29
37
|
'group-by': Q.struct.atomProperty.macromolecular.residueKey(),
|
|
30
38
|
}), structure);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { PairwiseAlignment } from '../mappings';
|
|
2
|
+
import type { AlignmentAlgorithm } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Whether a structure interaction (hover/click) should drive genome navigation.
|
|
5
|
+
* Only the transcript's mapped entity may — a hover on any other chain carries
|
|
6
|
+
* that chain's own label_seq_id and would mis-map through the wrong alignment.
|
|
7
|
+
* A structure with no resolved mapping (`mappedEntityId` undefined, e.g. a
|
|
8
|
+
* standalone viewer with no transcript) stays fully interactive.
|
|
9
|
+
*/
|
|
10
|
+
export declare function interactionMatchesMappedEntity(entityId: string, mappedEntityId: string | undefined): boolean;
|
|
11
|
+
export interface EntitySelection {
|
|
12
|
+
/** index into the entity-sequence array that best matches the transcript */
|
|
13
|
+
index: number;
|
|
14
|
+
/** alignment of the transcript against the chosen entity (stop codons
|
|
15
|
+
* stripped on both sides, matching the rest of the mapping pipeline) */
|
|
16
|
+
alignment: PairwiseAlignment;
|
|
17
|
+
/** identical aligned residues, the score used to pick the entity */
|
|
18
|
+
matches: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Pick which polymer entity of a structure corresponds to the transcript.
|
|
22
|
+
*
|
|
23
|
+
* The plugin historically hardcoded entity `[0]`, which silently mis-maps every
|
|
24
|
+
* heteromeric / protein-DNA / processed-peptide structure where the protein of
|
|
25
|
+
* interest is some other chain. Selecting by alignment makes the structure self-
|
|
26
|
+
* describe which entity is the gene's protein: an exact sequence match wins
|
|
27
|
+
* outright, otherwise the entity with the most identical aligned residues.
|
|
28
|
+
*
|
|
29
|
+
* Returns `undefined` only when there is nothing to map (no transcript or no
|
|
30
|
+
* entities) — never a silent fallback to the wrong entity.
|
|
31
|
+
*/
|
|
32
|
+
export declare function chooseMappedEntity(transcript: string, entitySeqs: string[], algorithm: AlignmentAlgorithm): EntitySelection | undefined;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { runLocalAlignment } from './pairwiseAlignment';
|
|
2
|
+
import { stripStopCodon } from '../LaunchProteinView/utils/util';
|
|
3
|
+
/**
|
|
4
|
+
* Whether a structure interaction (hover/click) should drive genome navigation.
|
|
5
|
+
* Only the transcript's mapped entity may — a hover on any other chain carries
|
|
6
|
+
* that chain's own label_seq_id and would mis-map through the wrong alignment.
|
|
7
|
+
* A structure with no resolved mapping (`mappedEntityId` undefined, e.g. a
|
|
8
|
+
* standalone viewer with no transcript) stays fully interactive.
|
|
9
|
+
*/
|
|
10
|
+
export function interactionMatchesMappedEntity(entityId, mappedEntityId) {
|
|
11
|
+
return mappedEntityId === undefined || entityId === mappedEntityId;
|
|
12
|
+
}
|
|
13
|
+
function countMatches(pa) {
|
|
14
|
+
const a = pa.alns[0].seq;
|
|
15
|
+
const b = pa.alns[1].seq;
|
|
16
|
+
let matches = 0;
|
|
17
|
+
for (let i = 0; i < a.length; i++) {
|
|
18
|
+
const ca = a[i];
|
|
19
|
+
const cb = b[i];
|
|
20
|
+
if (ca !== '-' && cb !== '-' && ca?.toUpperCase() === cb?.toUpperCase()) {
|
|
21
|
+
matches++;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return matches;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Pick which polymer entity of a structure corresponds to the transcript.
|
|
28
|
+
*
|
|
29
|
+
* The plugin historically hardcoded entity `[0]`, which silently mis-maps every
|
|
30
|
+
* heteromeric / protein-DNA / processed-peptide structure where the protein of
|
|
31
|
+
* interest is some other chain. Selecting by alignment makes the structure self-
|
|
32
|
+
* describe which entity is the gene's protein: an exact sequence match wins
|
|
33
|
+
* outright, otherwise the entity with the most identical aligned residues.
|
|
34
|
+
*
|
|
35
|
+
* Returns `undefined` only when there is nothing to map (no transcript or no
|
|
36
|
+
* entities) — never a silent fallback to the wrong entity.
|
|
37
|
+
*/
|
|
38
|
+
export function chooseMappedEntity(transcript, entitySeqs, algorithm) {
|
|
39
|
+
const t = stripStopCodon(transcript);
|
|
40
|
+
if (!t || entitySeqs.length === 0) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
const stripped = entitySeqs.map(stripStopCodon);
|
|
44
|
+
const exactIndex = stripped.findIndex(s => s.length > 0 && s === t);
|
|
45
|
+
if (exactIndex !== -1) {
|
|
46
|
+
return {
|
|
47
|
+
index: exactIndex,
|
|
48
|
+
alignment: {
|
|
49
|
+
consensus: '|'.repeat(t.length),
|
|
50
|
+
alns: [
|
|
51
|
+
{ id: 'seq1', seq: t },
|
|
52
|
+
{ id: 'seq2', seq: stripped[exactIndex] },
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
matches: t.length,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
let best;
|
|
59
|
+
for (let index = 0; index < stripped.length; index++) {
|
|
60
|
+
const s = stripped[index];
|
|
61
|
+
if (s.length === 0) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const alignment = runLocalAlignment(t, s, algorithm);
|
|
65
|
+
const matches = countMatches(alignment);
|
|
66
|
+
if (!best || matches > best.matches) {
|
|
67
|
+
best = { index, alignment, matches };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return best;
|
|
71
|
+
}
|
|
@@ -56,6 +56,7 @@ const FeatureBar = observer(function FeatureBar({ feature, model, }) {
|
|
|
56
56
|
structure,
|
|
57
57
|
plugin: molstarPluginContext,
|
|
58
58
|
channel: 'select',
|
|
59
|
+
entityId: model.mappedEntityId,
|
|
59
60
|
spec: newSelected
|
|
60
61
|
? { kind: 'range', start: feature.start - 1, end: feature.end }
|
|
61
62
|
: undefined,
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
/** A polymer entity of a loaded structure: its mmCIF entity id and its full
|
|
2
|
+
* (label_seq_id-indexed) one-letter sequence. The entity id is what lets every
|
|
3
|
+
* downstream step talk about "the gene's protein" by identity instead of by the
|
|
4
|
+
* fragile entity-[0] position. */
|
|
5
|
+
export interface Entity {
|
|
6
|
+
entityId: string;
|
|
7
|
+
seq: string;
|
|
8
|
+
}
|
|
1
9
|
interface StructureModel {
|
|
2
10
|
obj?: {
|
|
3
11
|
data: {
|
|
4
12
|
sequence: {
|
|
5
13
|
sequences: readonly {
|
|
14
|
+
entityId: string;
|
|
6
15
|
sequence: {
|
|
7
16
|
label: {
|
|
8
17
|
toArray(): ArrayLike<string>;
|
|
@@ -13,5 +22,8 @@ interface StructureModel {
|
|
|
13
22
|
};
|
|
14
23
|
};
|
|
15
24
|
}
|
|
25
|
+
export declare function extractEntities(model: StructureModel): Entity[] | undefined;
|
|
26
|
+
/** Back-compat helper for callers that only need the sequence strings (e.g. the
|
|
27
|
+
* launch dialog's isoform matching). */
|
|
16
28
|
export declare function extractStructureSequences(model: StructureModel): string[] | undefined;
|
|
17
29
|
export {};
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
export function extractEntities(model) {
|
|
2
|
+
return model.obj?.data.sequence.sequences.map(s => ({
|
|
3
|
+
entityId: s.entityId,
|
|
4
|
+
seq: Array.from(s.sequence.label.toArray()).join(''),
|
|
5
|
+
}));
|
|
6
|
+
}
|
|
7
|
+
/** Back-compat helper for callers that only need the sequence strings (e.g. the
|
|
8
|
+
* launch dialog's isoform matching). */
|
|
1
9
|
export function extractStructureSequences(model) {
|
|
2
|
-
return model
|
|
10
|
+
return extractEntities(model)?.map(e => e.seq);
|
|
3
11
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { addStructureFromData } from './addStructureFromData';
|
|
2
2
|
import { addStructureFromURL } from './addStructureFromURL';
|
|
3
3
|
import { extractPerResidueConfidence } from './extractPerResidueConfidence';
|
|
4
|
-
import {
|
|
4
|
+
import { extractEntities } from './extractStructureSequences';
|
|
5
5
|
/**
|
|
6
6
|
* Loads a structure (from inline data or a URL) into the given Molstar plugin
|
|
7
7
|
* and pulls out its per-chain sequences and per-residue confidence. Pure with
|
|
@@ -14,9 +14,9 @@ export async function loadStructureData({ structure, plugin, }) {
|
|
|
14
14
|
: structure.url
|
|
15
15
|
? await addStructureFromURL({ url: structure.url, plugin })
|
|
16
16
|
: { model: undefined };
|
|
17
|
-
const
|
|
17
|
+
const entities = model ? extractEntities(model) : undefined;
|
|
18
18
|
const confidence = model
|
|
19
|
-
? extractPerResidueConfidence(model,
|
|
19
|
+
? extractPerResidueConfidence(model, entities?.[0]?.seq.length)
|
|
20
20
|
: undefined;
|
|
21
|
-
return {
|
|
21
|
+
return { entities, confidence };
|
|
22
22
|
}
|
|
@@ -47,10 +47,13 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
|
|
|
47
47
|
hoverPosition: {
|
|
48
48
|
structureSeqPos?: number;
|
|
49
49
|
code?: string;
|
|
50
|
-
chain
|
|
50
|
+
chain? /**
|
|
51
|
+
* #action
|
|
52
|
+
*/: string;
|
|
51
53
|
source: "structure" | "genome";
|
|
52
54
|
} | undefined;
|
|
53
|
-
|
|
55
|
+
entities: import("./extractStructureSequences").Entity[] | undefined;
|
|
56
|
+
mappedEntityIndex: number;
|
|
54
57
|
structureConfidence: number[] | undefined;
|
|
55
58
|
isMouseInAlignment: boolean;
|
|
56
59
|
loadedToMolstar: boolean;
|
|
@@ -62,7 +65,7 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
|
|
|
62
65
|
hiddenFeatureTypes: Set<string>;
|
|
63
66
|
} & {
|
|
64
67
|
setStructureData(data: {
|
|
65
|
-
|
|
68
|
+
entities?: import("./extractStructureSequences").Entity[];
|
|
66
69
|
confidence?: number[];
|
|
67
70
|
}): void;
|
|
68
71
|
hideFeatureType(type: string): void;
|
|
@@ -581,8 +584,13 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
|
|
|
581
584
|
}): void;
|
|
582
585
|
setGenomeHoveredPosition(structureSeqPos?: number): void;
|
|
583
586
|
setAlignment(r?: import("../mappings").PairwiseAlignment): void;
|
|
587
|
+
setMappedEntityIndex(n: number): void;
|
|
584
588
|
setIsMouseInAlignment(val: boolean): void;
|
|
585
589
|
} & {
|
|
590
|
+
readonly structureSequences: string[] | undefined;
|
|
591
|
+
readonly mappedEntity: import("./extractStructureSequences").Entity | undefined;
|
|
592
|
+
readonly mappedStructureSeq: string | undefined;
|
|
593
|
+
readonly mappedEntityId: string | undefined;
|
|
586
594
|
readonly uniprotId: string | undefined;
|
|
587
595
|
readonly coordinateMapper: import("./coordinates").CoordinateMapper | undefined;
|
|
588
596
|
readonly structureSeqToTranscriptSeqPosition: Record<number, number> | undefined;
|
|
@@ -795,10 +803,13 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
|
|
|
795
803
|
hoverPosition: {
|
|
796
804
|
structureSeqPos?: number;
|
|
797
805
|
code?: string;
|
|
798
|
-
chain
|
|
806
|
+
chain? /**
|
|
807
|
+
* #action
|
|
808
|
+
*/: string;
|
|
799
809
|
source: "structure" | "genome";
|
|
800
810
|
} | undefined;
|
|
801
|
-
|
|
811
|
+
entities: import("./extractStructureSequences").Entity[] | undefined;
|
|
812
|
+
mappedEntityIndex: number;
|
|
802
813
|
structureConfidence: number[] | undefined;
|
|
803
814
|
isMouseInAlignment: boolean;
|
|
804
815
|
loadedToMolstar: boolean;
|
|
@@ -810,7 +821,7 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
|
|
|
810
821
|
hiddenFeatureTypes: Set<string>;
|
|
811
822
|
} & {
|
|
812
823
|
setStructureData(data: {
|
|
813
|
-
|
|
824
|
+
entities?: import("./extractStructureSequences").Entity[];
|
|
814
825
|
confidence?: number[];
|
|
815
826
|
}): void;
|
|
816
827
|
hideFeatureType(type: string): void;
|
|
@@ -1329,8 +1340,13 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
|
|
|
1329
1340
|
}): void;
|
|
1330
1341
|
setGenomeHoveredPosition(structureSeqPos?: number): void;
|
|
1331
1342
|
setAlignment(r?: import("../mappings").PairwiseAlignment): void;
|
|
1343
|
+
setMappedEntityIndex(n: number): void;
|
|
1332
1344
|
setIsMouseInAlignment(val: boolean): void;
|
|
1333
1345
|
} & {
|
|
1346
|
+
readonly structureSequences: string[] | undefined;
|
|
1347
|
+
readonly mappedEntity: import("./extractStructureSequences").Entity | undefined;
|
|
1348
|
+
readonly mappedStructureSeq: string | undefined;
|
|
1349
|
+
readonly mappedEntityId: string | undefined;
|
|
1334
1350
|
readonly uniprotId: string | undefined;
|
|
1335
1351
|
readonly coordinateMapper: import("./coordinates").CoordinateMapper | undefined;
|
|
1336
1352
|
readonly structureSeqToTranscriptSeqPosition: Record<number, number> | undefined;
|
|
@@ -1422,10 +1438,13 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
|
|
|
1422
1438
|
hoverPosition: {
|
|
1423
1439
|
structureSeqPos?: number;
|
|
1424
1440
|
code?: string;
|
|
1425
|
-
chain
|
|
1441
|
+
chain? /**
|
|
1442
|
+
* #action
|
|
1443
|
+
*/: string;
|
|
1426
1444
|
source: "structure" | "genome";
|
|
1427
1445
|
} | undefined;
|
|
1428
|
-
|
|
1446
|
+
entities: import("./extractStructureSequences").Entity[] | undefined;
|
|
1447
|
+
mappedEntityIndex: number;
|
|
1429
1448
|
structureConfidence: number[] | undefined;
|
|
1430
1449
|
isMouseInAlignment: boolean;
|
|
1431
1450
|
loadedToMolstar: boolean;
|
|
@@ -1437,7 +1456,7 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
|
|
|
1437
1456
|
hiddenFeatureTypes: Set<string>;
|
|
1438
1457
|
} & {
|
|
1439
1458
|
setStructureData(data: {
|
|
1440
|
-
|
|
1459
|
+
entities?: import("./extractStructureSequences").Entity[];
|
|
1441
1460
|
confidence?: number[];
|
|
1442
1461
|
}): void;
|
|
1443
1462
|
hideFeatureType(type: string): void;
|
|
@@ -1956,8 +1975,13 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
|
|
|
1956
1975
|
}): void;
|
|
1957
1976
|
setGenomeHoveredPosition(structureSeqPos?: number): void;
|
|
1958
1977
|
setAlignment(r?: import("../mappings").PairwiseAlignment): void;
|
|
1978
|
+
setMappedEntityIndex(n: number): void;
|
|
1959
1979
|
setIsMouseInAlignment(val: boolean): void;
|
|
1960
1980
|
} & {
|
|
1981
|
+
readonly structureSequences: string[] | undefined;
|
|
1982
|
+
readonly mappedEntity: import("./extractStructureSequences").Entity | undefined;
|
|
1983
|
+
readonly mappedStructureSeq: string | undefined;
|
|
1984
|
+
readonly mappedEntityId: string | undefined;
|
|
1961
1985
|
readonly uniprotId: string | undefined;
|
|
1962
1986
|
readonly coordinateMapper: import("./coordinates").CoordinateMapper | undefined;
|
|
1963
1987
|
readonly structureSeqToTranscriptSeqPosition: Record<number, number> | undefined;
|
|
@@ -2,7 +2,7 @@ import type { PairwiseAlignment } from '../mappings';
|
|
|
2
2
|
import type { IAnyStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
3
3
|
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view';
|
|
4
4
|
interface GenomeToTranscriptSeqMapping {
|
|
5
|
-
|
|
5
|
+
p2gCodon: Record<number, number[]>;
|
|
6
6
|
strand: number;
|
|
7
7
|
refName: string;
|
|
8
8
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getSession } from '@jbrowse/core/util';
|
|
2
|
-
import {
|
|
2
|
+
import { codonGenomeSpan } from '../mappings';
|
|
3
3
|
/**
|
|
4
4
|
* Maps a protein structure position to genome coordinates
|
|
5
5
|
* @returns [start, end] tuple of genome coordinates, or undefined if mapping fails
|
|
@@ -9,12 +9,11 @@ export function proteinToGenomeMapping({ model, structureSeqPos, }) {
|
|
|
9
9
|
if (!genomeToTranscriptSeqMapping || !pairwiseAlignment) {
|
|
10
10
|
return undefined;
|
|
11
11
|
}
|
|
12
|
-
const {
|
|
12
|
+
const { p2gCodon } = genomeToTranscriptSeqMapping;
|
|
13
13
|
const transcriptPos = structureSeqToTranscriptSeqPosition?.[structureSeqPos];
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return getCodonRange(p2g, transcriptPos, strand);
|
|
14
|
+
return transcriptPos === undefined
|
|
15
|
+
? undefined
|
|
16
|
+
: codonGenomeSpan(p2gCodon, transcriptPos);
|
|
18
17
|
}
|
|
19
18
|
/**
|
|
20
19
|
* Maps a protein structure range to genome coordinates
|
|
@@ -63,7 +62,11 @@ export async function navigateToProteinPosition({ model, structureSeqPos, struct
|
|
|
63
62
|
}
|
|
64
63
|
const [start, end] = result;
|
|
65
64
|
if (zoomToBaseLevel) {
|
|
66
|
-
|
|
65
|
+
// start/end are 0-based half-open (from getCodonRanges). navToLocString
|
|
66
|
+
// parses a 1-based locString (parseLocString does start -= 1), so the start
|
|
67
|
+
// must be shifted to 1-based; the half-open end already equals the 1-based
|
|
68
|
+
// inclusive end. Passing the raw 0-based start landed the view 1bp 5'.
|
|
69
|
+
await connectedView.navToLocString(`${refName}:${start + 1}-${end}${strand === -1 ? '[rev]' : ''}`, undefined, 0.2);
|
|
67
70
|
}
|
|
68
71
|
else {
|
|
69
72
|
const { assemblyManager } = session;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Instance } from '@jbrowse/mobx-state-tree';
|
|
2
2
|
import { type CoordinateMapper } from './coordinates';
|
|
3
|
+
import type { Entity } from './extractStructureSequences';
|
|
3
4
|
import type { PairwiseAlignment } from '../mappings';
|
|
4
5
|
import type { AlignmentAlgorithm } from './types';
|
|
5
6
|
import type { SimpleFeatureSerialized } from '@jbrowse/core/util';
|
|
@@ -91,7 +92,15 @@ declare const Structure: import("@jbrowse/mobx-state-tree").IModelType<{
|
|
|
91
92
|
/**
|
|
92
93
|
* #volatile
|
|
93
94
|
*/
|
|
94
|
-
|
|
95
|
+
entities: Entity[] | undefined;
|
|
96
|
+
/**
|
|
97
|
+
* #volatile
|
|
98
|
+
* Index into entities of the one that matches the transcript. Resolved by
|
|
99
|
+
* alignment (see chooseMappedEntity) instead of assuming the protein of
|
|
100
|
+
* interest is entity [0], which mis-maps heteromers / protein-DNA complexes /
|
|
101
|
+
* processed peptides.
|
|
102
|
+
*/
|
|
103
|
+
mappedEntityIndex: number;
|
|
95
104
|
/**
|
|
96
105
|
* #volatile
|
|
97
106
|
* Per-residue B-factor / pLDDT for the first chain, indexed by 0-based
|
|
@@ -127,7 +136,7 @@ declare const Structure: import("@jbrowse/mobx-state-tree").IModelType<{
|
|
|
127
136
|
hiddenFeatureTypes: Set<string>;
|
|
128
137
|
} & {
|
|
129
138
|
setStructureData(data: {
|
|
130
|
-
|
|
139
|
+
entities?: Entity[];
|
|
131
140
|
confidence?: number[];
|
|
132
141
|
}): void;
|
|
133
142
|
/**
|
|
@@ -189,11 +198,37 @@ declare const Structure: import("@jbrowse/mobx-state-tree").IModelType<{
|
|
|
189
198
|
* #action
|
|
190
199
|
*/
|
|
191
200
|
setAlignment(r?: PairwiseAlignment): void;
|
|
201
|
+
/**
|
|
202
|
+
* #action
|
|
203
|
+
*/
|
|
204
|
+
setMappedEntityIndex(n: number): void;
|
|
192
205
|
/**
|
|
193
206
|
* #action
|
|
194
207
|
*/
|
|
195
208
|
setIsMouseInAlignment(val: boolean): void;
|
|
196
209
|
} & {
|
|
210
|
+
/**
|
|
211
|
+
* #getter
|
|
212
|
+
* Sequence strings of every polymer entity (back-compat for the alignment
|
|
213
|
+
* autorun and presence checks).
|
|
214
|
+
*/
|
|
215
|
+
readonly structureSequences: string[] | undefined;
|
|
216
|
+
/**
|
|
217
|
+
* #getter
|
|
218
|
+
* The entity that maps to the transcript (chosen by chooseMappedEntity), not
|
|
219
|
+
* blindly entity [0].
|
|
220
|
+
*/
|
|
221
|
+
readonly mappedEntity: Entity | undefined;
|
|
222
|
+
/**
|
|
223
|
+
* #getter
|
|
224
|
+
*/
|
|
225
|
+
readonly mappedStructureSeq: string | undefined;
|
|
226
|
+
/**
|
|
227
|
+
* #getter
|
|
228
|
+
* mmCIF entity id of the mapped entity. Used to reject hovers/clicks on
|
|
229
|
+
* other chains and to confine highlights to the gene's protein.
|
|
230
|
+
*/
|
|
231
|
+
readonly mappedEntityId: string | undefined;
|
|
197
232
|
/**
|
|
198
233
|
* #getter
|
|
199
234
|
* Extracts UniProt ID from AlphaFold URL if available
|
|
@@ -227,6 +262,9 @@ declare const Structure: import("@jbrowse/mobx-state-tree").IModelType<{
|
|
|
227
262
|
* #getter
|
|
228
263
|
* Per-residue pLDDT values mapped to alignment columns, shown only when the
|
|
229
264
|
* structure's B-factor column actually looks like AlphaFold confidence.
|
|
265
|
+
* pLDDT is an AlphaFold concept — those models are single-entity, so the
|
|
266
|
+
* mapped entity is always [0] and structureConfidence (extracted from [0])
|
|
267
|
+
* stays coherent.
|
|
230
268
|
*/
|
|
231
269
|
readonly confidenceCells: {
|
|
232
270
|
col: number;
|