Quellcode durchsuchen

Sequence identity MSA pagination keeps the state

bioinsilico vor 2 Jahren
Ursprung
Commit
fc9b327dd5

+ 5 - 4
CHANGELOG.md

@@ -2,12 +2,13 @@
 
 [Semantic Versioning](https://semver.org/)
 
-## [2.2.0-msa] - 2022-10-03
+## [2.2.0-msa] - 2022-10-13
 ### New Features
 - New UniProt MSA 1D3D view
-  - `UniprotPfvManagerFactory` builds the UniProt MSA PFV
-  - `UniprotCallbackManagerFactory` 1D callbacks
-  - `UniprotBehaviourObserver` 3D callbacks
+  - `UniprotPfvManagerFactory` builds UniProt Group MSA PFV
+  - `SequenceIdentityPfvManagerFactory` builds Sequence Identity MSA PFV
+  - `MsaCallbackManagerFactory` MSA 1D callbacks
+  - `MsaBehaviourObserver` MSA 3D callbacks
 
 ## [2.1.0] - 2022-09-02
 ### Major refactoring

+ 37 - 11
package-lock.json

@@ -1,18 +1,18 @@
 {
   "name": "@rcsb/rcsb-saguaro-3d",
-  "version": "2.2.0-uniprot-msa.8",
+  "version": "2.2.0-uniprot-msa.9",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "@rcsb/rcsb-saguaro-3d",
-      "version": "2.2.0-uniprot-msa.8",
+      "version": "2.2.0-uniprot-msa.9",
       "license": "MIT",
       "dependencies": {
         "@rcsb/rcsb-api-tools": "^4.1.0",
         "@rcsb/rcsb-molstar": "^2.5.5",
         "@rcsb/rcsb-saguaro": "^2.2.15",
-        "@rcsb/rcsb-saguaro-app": "^4.4.12",
+        "@rcsb/rcsb-saguaro-app": "^4.4.13-uniprot-msa.7",
         "molstar": "^3.13.0"
       },
       "devDependencies": {
@@ -2515,17 +2515,29 @@
       }
     },
     "node_modules/@rcsb/rcsb-saguaro-app": {
-      "version": "4.4.12",
-      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-saguaro-app/-/rcsb-saguaro-app-4.4.12.tgz",
-      "integrity": "sha512-4dR2mq2jFnHeTa8+QSWLpB0e1XsSZXMU5c7pKG3Frx0PzaQ2gP6yzrowxqrBjQuWbvUFJnpnIa1t3l7FKqJplw==",
+      "version": "4.4.13-uniprot-msa.7",
+      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-saguaro-app/-/rcsb-saguaro-app-4.4.13-uniprot-msa.7.tgz",
+      "integrity": "sha512-evnr2ullQ7z157dPyfIKmWpD3qL7igfjc/FtjBgitzFMZmZWleLLz3UJWs9guiZ5E8d/tpZiq+vsSUPJRFqscQ==",
       "dependencies": {
-        "@rcsb/rcsb-api-tools": "^4.1.0",
+        "@rcsb/rcsb-api-tools": "^4.1.1-uniprot-msa.1",
         "@rcsb/rcsb-saguaro": "^2.2.15",
         "react-select": "^5.4.0",
         "rxjs": "^7.5.5",
         "victory": "^36.5.0"
       }
     },
+    "node_modules/@rcsb/rcsb-saguaro-app/node_modules/@rcsb/rcsb-api-tools": {
+      "version": "4.1.1-uniprot-msa.1",
+      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-api-tools/-/rcsb-api-tools-4.1.1-uniprot-msa.1.tgz",
+      "integrity": "sha512-5//qmrcwe50M5Fv3b4nHbAEr0WHHJbKfiEZ0m0lY8u0utpJlV/8cSjzgQMI5OCoWJC/wqeglNPxEQ8Mpt9nnZg==",
+      "dependencies": {
+        "@graphql-codegen/cli": "^2.6.2",
+        "graphql": "^15.8.0",
+        "graphql-request": "^4.2.0",
+        "lzutf8": "^0.6.1",
+        "object-hash": "^3.0.0"
+      }
+    },
     "node_modules/@tootallnate/once": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
@@ -14088,15 +14100,29 @@
       }
     },
     "@rcsb/rcsb-saguaro-app": {
-      "version": "4.4.12",
-      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-saguaro-app/-/rcsb-saguaro-app-4.4.12.tgz",
-      "integrity": "sha512-4dR2mq2jFnHeTa8+QSWLpB0e1XsSZXMU5c7pKG3Frx0PzaQ2gP6yzrowxqrBjQuWbvUFJnpnIa1t3l7FKqJplw==",
+      "version": "4.4.13-uniprot-msa.7",
+      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-saguaro-app/-/rcsb-saguaro-app-4.4.13-uniprot-msa.7.tgz",
+      "integrity": "sha512-evnr2ullQ7z157dPyfIKmWpD3qL7igfjc/FtjBgitzFMZmZWleLLz3UJWs9guiZ5E8d/tpZiq+vsSUPJRFqscQ==",
       "requires": {
-        "@rcsb/rcsb-api-tools": "^4.1.0",
+        "@rcsb/rcsb-api-tools": "^4.1.1-uniprot-msa.1",
         "@rcsb/rcsb-saguaro": "^2.2.15",
         "react-select": "^5.4.0",
         "rxjs": "^7.5.5",
         "victory": "^36.5.0"
+      },
+      "dependencies": {
+        "@rcsb/rcsb-api-tools": {
+          "version": "4.1.1-uniprot-msa.1",
+          "resolved": "https://registry.npmjs.org/@rcsb/rcsb-api-tools/-/rcsb-api-tools-4.1.1-uniprot-msa.1.tgz",
+          "integrity": "sha512-5//qmrcwe50M5Fv3b4nHbAEr0WHHJbKfiEZ0m0lY8u0utpJlV/8cSjzgQMI5OCoWJC/wqeglNPxEQ8Mpt9nnZg==",
+          "requires": {
+            "@graphql-codegen/cli": "^2.6.2",
+            "graphql": "^15.8.0",
+            "graphql-request": "^4.2.0",
+            "lzutf8": "^0.6.1",
+            "object-hash": "^3.0.0"
+          }
+        }
       }
     },
     "@tootallnate/once": {

+ 2 - 2
package.json

@@ -1,6 +1,6 @@
 {
   "name": "@rcsb/rcsb-saguaro-3d",
-  "version": "2.2.0-uniprot-msa.8",
+  "version": "2.2.0-uniprot-msa.9",
   "description": "RCSB Molstar/Saguaro Web App",
   "main": "build/dist/app.js",
   "files": [
@@ -83,7 +83,7 @@
     "@rcsb/rcsb-api-tools": "^4.1.0",
     "@rcsb/rcsb-molstar": "^2.5.5",
     "@rcsb/rcsb-saguaro": "^2.2.15",
-    "@rcsb/rcsb-saguaro-app": "^4.4.12",
+    "@rcsb/rcsb-saguaro-app": "^4.4.13-uniprot-msa.7",
     "molstar": "^3.13.0"
   },
   "bugs": {

+ 7 - 4
src/RcsbFv3D/RcsbFv3DSequenceIdentity.tsx

@@ -22,18 +22,20 @@ import {
 import {RcsbFvStructure} from "../RcsbFvStructure/RcsbFvStructure";
 import {RcsbFv3DCssConfig} from "./RcsbFv3DComponent";
 import {MolstarAlignmentLoader} from "../RcsbFvStructure/StructureUtils/MolstarAlignmentLoader";
-import {UniprotBehaviourObserver} from "../RcsbFvStructure/StructureViewerBehaviour/UniprotBehaviour";
+import {MsaBehaviourObserver} from "../RcsbFvStructure/StructureViewerBehaviour/MsaBehaviour";
 import {
     SequenceIdentityPfvManagerFactory
 } from "../RcsbFvSequence/SequenceViews/RcsbView/PfvManagerFactoryImplementation/SequenceIdentityPfvManagerFactory";
 import {
     PolymerEntityInstanceInterface
 } from "@rcsb/rcsb-saguaro-app/build/dist/RcsbCollectTools/DataCollectors/PolymerEntityInstancesCollector";
+import {SearchQuery} from "@rcsb/rcsb-api-tools/build/RcsbSearch/Types/SearchQueryInterface";
 
 export interface RcsbFv3DSequenceIdentityInterface  {
     elementId?: string;
     config: {
         groupId: string;
+        query?: SearchQuery;
         title?: string;
         subtitle?: string;
     };
@@ -42,7 +44,7 @@ export interface RcsbFv3DSequenceIdentityInterface  {
     cssConfig?: RcsbFv3DCssConfig;
 }
 
-export class RcsbFv3DSequenceIdentity extends RcsbFv3DAbstract<{groupId:string},LoadMolstarInterface|undefined,{viewerElement:string|HTMLElement,viewerProps:Partial<ViewerProps>},{context:any,module:RcsbFvModulePublicInterface}> {
+export class RcsbFv3DSequenceIdentity extends RcsbFv3DAbstract<{groupId:string; query?: SearchQuery;},LoadMolstarInterface|undefined,{viewerElement:string|HTMLElement,viewerProps:Partial<ViewerProps>},{context:any,module:RcsbFvModulePublicInterface}> {
     constructor(params:RcsbFv3DSequenceIdentityInterface){
         const elementId: string = params.elementId ?? uniqid("RcsbFv3D_");
         super({
@@ -55,7 +57,8 @@ export class RcsbFv3DSequenceIdentity extends RcsbFv3DAbstract<{groupId:string},
                     rcsbId: params.config.groupId,
                     additionalConfig: params.additionalConfig,
                     pfvParams:{
-                        groupId:params.config.groupId
+                        groupId:params.config.groupId,
+                        query:params.config.query
                     },
                     buildPfvOnMount: true,
                     pfvManagerFactory: new SequenceIdentityPfvManagerFactory<LoadMolstarInterface>(),
@@ -70,7 +73,7 @@ export class RcsbFv3DSequenceIdentity extends RcsbFv3DAbstract<{groupId:string},
                 }
             },
             structureViewer: new StructureViewer<LoadMolstarInterface,{viewerElement:string|HTMLElement,viewerProps:Partial<ViewerProps>}>( new MolstarManagerFactory() ),
-            structureViewerBehaviourObserver: new UniprotBehaviourObserver<LoadMolstarInterface>(new MolstarAlignmentLoader())
+            structureViewerBehaviourObserver: new MsaBehaviourObserver<LoadMolstarInterface>(new MolstarAlignmentLoader())
         });
     }
 

+ 7 - 4
src/RcsbFv3D/RcsbFv3DUniprot.tsx

@@ -22,12 +22,14 @@ import {
 import {RcsbFvStructure} from "../RcsbFvStructure/RcsbFvStructure";
 import {RcsbFv3DCssConfig} from "./RcsbFv3DComponent";
 import {MolstarAlignmentLoader} from "../RcsbFvStructure/StructureUtils/MolstarAlignmentLoader";
-import {UniprotBehaviourObserver} from "../RcsbFvStructure/StructureViewerBehaviour/UniprotBehaviour";
+import {MsaBehaviourObserver} from "../RcsbFvStructure/StructureViewerBehaviour/MsaBehaviour";
+import {SearchQuery} from "@rcsb/rcsb-api-tools/build/RcsbSearch/Types/SearchQueryInterface";
 
 export interface RcsbFv3DUniprotInterface  {
     elementId?: string;
     config: {
         upAcc: string;
+        query?: SearchQuery
         title?: string;
         subtitle?: string;
     };
@@ -36,7 +38,7 @@ export interface RcsbFv3DUniprotInterface  {
     cssConfig?: RcsbFv3DCssConfig;
 }
 
-export class RcsbFv3DUniprot extends RcsbFv3DAbstract<{upAcc:string},LoadMolstarInterface|undefined,{viewerElement:string|HTMLElement,viewerProps:Partial<ViewerProps>},{context:UniprotSequenceOnchangeInterface,module:RcsbFvModulePublicInterface}> {
+export class RcsbFv3DUniprot extends RcsbFv3DAbstract<{upAcc:string; query?: SearchQuery;},LoadMolstarInterface|undefined,{viewerElement:string|HTMLElement,viewerProps:Partial<ViewerProps>},{context:UniprotSequenceOnchangeInterface,module:RcsbFvModulePublicInterface}> {
     constructor(params:RcsbFv3DUniprotInterface){
         const elementId: string = params.elementId ?? uniqid("RcsbFv3D_");
         super({
@@ -49,7 +51,8 @@ export class RcsbFv3DUniprot extends RcsbFv3DAbstract<{upAcc:string},LoadMolstar
                     rcsbId: params.config.upAcc,
                     additionalConfig: params.additionalConfig,
                     pfvParams:{
-                        upAcc:params.config.upAcc
+                        upAcc:params.config.upAcc,
+                        query:params.config.query
                     },
                     buildPfvOnMount: true,
                     pfvManagerFactory: new UniprotPfvManagerFactory<LoadMolstarInterface>(),
@@ -64,7 +67,7 @@ export class RcsbFv3DUniprot extends RcsbFv3DAbstract<{upAcc:string},LoadMolstar
                 }
             },
             structureViewer: new StructureViewer<LoadMolstarInterface,{viewerElement:string|HTMLElement,viewerProps:Partial<ViewerProps>}>( new MolstarManagerFactory() ),
-            structureViewerBehaviourObserver: new UniprotBehaviourObserver<LoadMolstarInterface>(new MolstarAlignmentLoader())
+            structureViewerBehaviourObserver: new MsaBehaviourObserver<LoadMolstarInterface>(new MolstarAlignmentLoader())
         });
     }
 

+ 30 - 5
src/RcsbFvSequence/SequenceViews/RcsbView/CallbackManagerFactoryImplementation/MsaCallbackManager.ts

@@ -4,7 +4,11 @@ import {
     CallbackManagerFactoryInterface, CallbackManagerInterface
 } from "../CallbackManagerFactoryInterface";
 import {RcsbFvTrackDataElementInterface} from "@rcsb/rcsb-saguaro";
-import {AlignedRegion, AlignmentResponse} from "@rcsb/rcsb-api-tools/build/RcsbGraphQL/Types/Borrego/GqlTypes";
+import {
+    AlignedRegion,
+    AlignmentResponse,
+    TargetAlignment
+} from "@rcsb/rcsb-api-tools/build/RcsbGraphQL/Types/Borrego/GqlTypes";
 import {RegionSelectionInterface} from "../../../../RcsbFvState/RcsbFvSelectorManager";
 import {ChainInfo, SaguaroRegionList} from "../../../../RcsbFvStructure/StructureViewerInterface";
 import {TagDelimiter} from "@rcsb/rcsb-saguaro-app";
@@ -27,6 +31,8 @@ type SelectedRegion = {modelId: string, labelAsymId: string, region: RegionSelec
 class MsaCallbackManager<R,U>  extends AbstractCallbackManager<R,U>{
 
     private readonly loadParamRequest:(id: string)=>R;
+    private readonly targetIds: {[key:string]:boolean} = {};
+    private alignmentResponse: AlignmentResponse;
 
     constructor(config: CallbackConfigInterface<R> & {loadParamRequest:(id: string)=>R}) {
         super(config);
@@ -52,7 +58,22 @@ class MsaCallbackManager<R,U>  extends AbstractCallbackManager<R,U>{
     async pfvChangeCallback(params:U): Promise<void> {
         if(typeof this.rcsbFvContainer.get() === "undefined")
             return;
-        return Promise.resolve(undefined);
+        const alignmentResponse: AlignmentResponse|undefined = await this.rcsbFvContainer.get()?.getAlignmentResponse();
+        if(!this.alignmentResponse && alignmentResponse) {
+            this.alignmentResponse = alignmentResponse;
+            alignmentResponse.target_alignment?.forEach(ta=> {if(ta?.target_id) this.targetIds[ta.target_id]=true})
+        }else if(alignmentResponse) {
+            const newTargetAlignments = alignmentResponse.target_alignment?.filter(ta=>{
+                if(ta && ta.target_id && !this.targetIds[ta.target_id]){
+                    this.targetIds[ta.target_id] = true;
+                    return true;
+                }
+            });
+            if(newTargetAlignments)
+                this.alignmentResponse.target_alignment = this.alignmentResponse.target_alignment?.concat(
+                    newTargetAlignments
+                );
+        }
     }
 
     protected async innerStructureViewerSelectionChange(mode: "select" | "hover"): Promise<void> {
@@ -82,8 +103,9 @@ class MsaCallbackManager<R,U>  extends AbstractCallbackManager<R,U>{
         if(alignment){
             const regions = this.getModelRegions(selection, alignment, Array.from(this.stateManager.assemblyModelSate.getMap().keys()), "query");
             if(regions.length == 0)
-                this.stateManager.selectionState.clearSelection("select");
-            this.stateManager.selectionState.selectFromMultipleRegions("set", regions, mode);
+                this.stateManager.selectionState.clearSelection(mode);
+            else
+                this.stateManager.selectionState.selectFromMultipleRegions("set", regions, mode);
             this.stateManager.next({type: mode == "select" ? "selection-change" : "hover-change", view:"1d-view"});
         }
     }
@@ -99,7 +121,10 @@ class MsaCallbackManager<R,U>  extends AbstractCallbackManager<R,U>{
             if(!labelAsymId || ! operatorName)
                 return;
             selection.forEach(s=>{
-                const alignedRegions = alignment.target_alignment?.find(ta=>ta?.target_id === modelId)?.aligned_regions!.filter((o): o is AlignedRegion => o!=null);
+                const alignedRegions = (alignment.target_alignment?.find(ta=>ta?.target_id === modelId)?.aligned_regions!.filter((o): o is AlignedRegion => o!=null) ?? []).concat(
+                    this.alignmentResponse?.target_alignment?.find(ta=>ta?.target_id === modelId)?.aligned_regions!.filter((o): o is AlignedRegion => o!=null) ?? []
+                );
+
                 if(!alignedRegions)
                     return;
                 AM.mapRangeToRegionList({begin:s.begin, end: s.end ?? s.begin}, alignedRegions, pointer)?.forEach(region=>{

+ 2 - 1
src/RcsbFvSequence/SequenceViews/RcsbView/PfvManagerFactoryImplementation/MsaPfvComponents/MsaRowMarkComponent.tsx

@@ -52,6 +52,7 @@ export class MsaRowMarkComponent extends React.Component <MsaRowMarkInterface,Ms
 
     componentDidMount() {
         this.subscribe();
+        this.modelChange();
     }
 
     componentWillUnmount() {
@@ -66,7 +67,7 @@ export class MsaRowMarkComponent extends React.Component <MsaRowMarkInterface,Ms
     }
 
     private modelChange(): void {
-       if(Array.from(this.props.stateManager.assemblyModelSate.getMap().keys()).includes(`${this.props.rowRef.entryId}${TagDelimiter.entity}${this.props.rowRef.entityId}`))
+       if(Array.from(this.props.stateManager.assemblyModelSate.getMap()?.keys() ?? []).includes(`${this.props.rowRef.entryId}${TagDelimiter.entity}${this.props.rowRef.entityId}`))
            this.setState({visibility: "visible", borderLeftColor: this.ACTIVE_COLOR});
        else if(this.state.visibility == "visible")
            this.setState({visibility: undefined, borderLeftColor: undefined});

+ 39 - 7
src/RcsbFvSequence/SequenceViews/RcsbView/PfvManagerFactoryImplementation/MsaPfvComponents/MsaRowTitleCheckbox.tsx

@@ -20,27 +20,42 @@ interface MsaRowTitleCheckboxState {
     checked:boolean;
 }
 
+//TODO keeps a global state of the (checkboxes <=> mol-star components) This needs further review!!!
+const globalState: {[key:string]: boolean;} = {};
+
 export class MsaRowTitleCheckbox extends React.Component <MsaRowTitleCheckboxInterface,MsaRowTitleCheckboxState> {
 
     readonly state: MsaRowTitleCheckboxState = {
-        checked: this.props.tag == "aligned"
+        checked: typeof globalState[ this.entityId()+this.props.tag ] === "boolean" ? globalState[ this.entityId()+this.props.tag ] : this.props.tag == "aligned"
     };
 
     private subscription: Subscription;
 
     constructor(props: MsaRowTitleCheckboxInterface) {
         super(props);
-        this.subscribe();
     }
 
-
     public render():JSX.Element {
-        return (<div style={this.style()} onClick={()=>{this.click()}}/>);
+        return (
+            <div
+                style={this.style()}
+                onClick={()=>{this.click()}}
+                title={this.title()}
+            />);
+    }
+
+    public componentDidMount() {
+        this.subscribe();
     }
 
     public componentDidUpdate(prevProps: Readonly<MsaRowTitleCheckboxInterface>, prevState: Readonly<MsaRowTitleCheckboxState>, snapshot?: any) {
-        if(prevProps.disabled != this.props.disabled)
-            this.setState({checked:(!this.props.disabled && this.props.tag == "aligned")});
+        if(prevProps.disabled != this.props.disabled && !this.props.disabled ) {
+           this.setState({checked: typeof globalState[ this.entityId()+this.props.tag ] === "boolean" ? globalState[ this.entityId()+this.props.tag ] : (!this.props.disabled && this.props.tag == "aligned")});
+        }else if(prevProps.disabled != this.props.disabled) {
+            this.setState({checked: this.props.tag == "aligned"},()=>{
+                globalState[ this.entityId()+this.props.tag ]  = this.state.checked;
+            });
+        }
     }
 
     public componentWillUnmount() {
@@ -59,7 +74,7 @@ export class MsaRowTitleCheckbox extends React.Component <MsaRowTitleCheckboxInt
         const suffix: string = row.pop()!;
         const entryId: string = row.join(TagDelimiter.entity);
         const entityId: string = suffix.substring(0,1);
-        if( `${this.props.entryId}${TagDelimiter.entity}${this.props.entityId}` == `${entryId}${TagDelimiter.entity}${entityId}` ){
+        if( this.entityId() == `${entryId}${TagDelimiter.entity}${entityId}` ){
             //TODO this is a one to many relationship
             /*if( d.label.includes("polymer") && this.props.tag == "polymer" && d.isHidden == this.state.checked){
                 this.setState({checked:!this.state.checked});
@@ -73,6 +88,7 @@ export class MsaRowTitleCheckbox extends React.Component <MsaRowTitleCheckboxInt
 
     private click(): void {
         this.setState({checked:!this.state.checked},()=>{
+            globalState[this.entityId()+this.props.tag] = this.state.checked;
             this.props.stateManager.next<"representation-change",{tag:MsaRowTitleCheckboxInterface["tag"];isHidden:boolean;pdb:{entryId:string;entityId:string;};}>({
                 view:"1d-view",
                 type: "representation-change",
@@ -107,4 +123,20 @@ export class MsaRowTitleCheckbox extends React.Component <MsaRowTitleCheckboxInt
         };
     }
 
+    private entityId(): string {
+        return `${this.props.entryId}${TagDelimiter.entity}${this.props.entityId}`;
+    };
+
+    private title(): string | undefined{
+        if(this.props.disabled)
+            return undefined;
+        switch (this.props.tag){
+            case "aligned":
+                return `${this.state.checked ? "Hide" : "Show"} Aligned Polymer Chain`;
+            case "polymer":
+                return `${this.state.checked ? "Hide" : "Show"} Other Polymer Chains`;
+            case "non-polymer":
+                return `${this.state.checked ? "Hide" : "Show"} Non-polymer Chains`;
+        }
+    }
 }

+ 36 - 17
src/RcsbFvSequence/SequenceViews/RcsbView/PfvManagerFactoryImplementation/MsaPfvComponents/MsaRowTitleComponent.tsx

@@ -48,23 +48,39 @@ export class MsaRowTitleComponent extends React.Component <MsaRowTitleInterface,
     }
 
     public render(): JSX.Element{
-       return <div style={{textAlign:"right"}}>
-           <div style={{
-               MozUserSelect:"none",
-               WebkitUserSelect:"none",
-               msUserSelect:"none",
-               display:"inline-block",
-               color: this.state.titleColor,
-               cursor: "pointer"
-           }} onClick={(e: MouseEvent)=>this.click(e)} onMouseOver={()=>this.hover(true)} onMouseOut={()=>this.hover(false)}>{this.props.targetAlignment.target_id}</div>
-           <MsaRowTitleCheckbox disabled={this.state.disabled} {...TagDelimiter.parseEntity(this.props.targetAlignment.target_id!)} tag={"aligned"} stateManager={this.props.stateManager}/>
-           <MsaRowTitleCheckbox disabled={this.state.disabled} {...TagDelimiter.parseEntity(this.props.targetAlignment.target_id!)} tag={"polymer"} stateManager={this.props.stateManager}/>
-           <MsaRowTitleCheckbox disabled={this.state.disabled} {...TagDelimiter.parseEntity(this.props.targetAlignment.target_id!)} tag={"non-polymer"} stateManager={this.props.stateManager}/>
-       </div>;
+       return (
+           <div style={{textAlign:"right", display:"flex"}}>
+               <div >
+                   <div
+                       style={{
+                           MozUserSelect:"none",
+                           WebkitUserSelect:"none",
+                           msUserSelect:"none",
+                           color: this.state.titleColor,
+                           cursor: "pointer",
+                           maxWidth:100,
+                           overflow: "hidden",
+                           textOverflow: "ellipsis",
+                           whiteSpace: "nowrap"
+                       }}
+                       onClick={(e: MouseEvent)=>this.click(e)}
+                       onMouseOver={()=>this.hover(true)}
+                       onMouseOut={()=>this.hover(false)}
+                       title={this.props.targetAlignment.target_id ?? undefined}
+                   >
+                       {this.props.targetAlignment.target_id}
+                   </div>
+               </div>
+               <div><MsaRowTitleCheckbox disabled={this.state.disabled} {...TagDelimiter.parseEntity(this.props.targetAlignment.target_id!)} tag={"aligned"} stateManager={this.props.stateManager}/></div>
+               <div><MsaRowTitleCheckbox disabled={this.state.disabled} {...TagDelimiter.parseEntity(this.props.targetAlignment.target_id!)} tag={"polymer"} stateManager={this.props.stateManager}/></div>
+               <div><MsaRowTitleCheckbox disabled={this.state.disabled} {...TagDelimiter.parseEntity(this.props.targetAlignment.target_id!)} tag={"non-polymer"} stateManager={this.props.stateManager}/></div>
+           </div>
+       );
     }
 
     public componentDidMount(): void {
         this.subscribe();
+        this.modelChange();
     }
 
     public componentWillUnmount() {
@@ -79,7 +95,7 @@ export class MsaRowTitleComponent extends React.Component <MsaRowTitleInterface,
     }
 
     private modelChange(): void {
-        if(this.props.targetAlignment.target_id && this.props.stateManager.assemblyModelSate.getMap().has(this.props.targetAlignment.target_id)){
+        if(this.props.targetAlignment.target_id && this.props.stateManager.assemblyModelSate.getMap()?.has(this.props.targetAlignment.target_id)){
             if(this.state.disabled)
                 this.setState({disabled:false, titleColor:this.ACTIVE_COLOR});
         }else if(!this.state.disabled){
@@ -95,10 +111,13 @@ export class MsaRowTitleComponent extends React.Component <MsaRowTitleInterface,
     }
 
     private click(e: MouseEvent){
-        if(e.shiftKey)
-            document.location.href = `/structure/${TagDelimiter.parseEntity(this.props.targetAlignment.target_id!).entryId}#entity-${TagDelimiter.parseEntity(this.props.targetAlignment.target_id!).entityId}`;
-        else
+        if(e.shiftKey) {
+            const newWin: Window|null = window.open(`/structure/${TagDelimiter.parseEntity(this.props.targetAlignment.target_id!).entryId}#entity-${TagDelimiter.parseEntity(this.props.targetAlignment.target_id!).entityId}`);
+            if(!newWin || newWin.closed || typeof newWin.closed=='undefined')
+                document.location.href = `/structure/${TagDelimiter.parseEntity(this.props.targetAlignment.target_id!).entryId}#entity-${TagDelimiter.parseEntity(this.props.targetAlignment.target_id!).entityId}`;
+        } else {
             this.props.titleClick();
+        }
     }
 
 }

+ 14 - 11
src/RcsbFvSequence/SequenceViews/RcsbView/PfvManagerFactoryImplementation/SequenceIdentityPfvManagerFactory.ts

@@ -18,9 +18,11 @@ import {MsaRowMarkComponent} from "./MsaPfvComponents/MsaRowMarkComponent";
 import {
     PolymerEntityInstanceInterface
 } from "@rcsb/rcsb-saguaro-app/build/dist/RcsbCollectTools/DataCollectors/PolymerEntityInstancesCollector";
+import {SearchQuery} from "@rcsb/rcsb-api-tools/build/RcsbSearch/Types/SearchQueryInterface";
 
 interface SequenceIdentityPfvManagerInterface<R> extends PfvManagerFactoryConfigInterface<R,{context: {groupId:string};}> {
     groupId:string;
+    query?: SearchQuery;
 }
 
 export class SequenceIdentityPfvManagerFactory<R> implements PfvManagerFactoryInterface<{groupId:string},R,{context: {groupId:string};}> {
@@ -41,20 +43,18 @@ type AlignmentDataType = {
 
 class SequenceIdentityPfvManager<R> extends AbstractPfvManager<{groupId:string},R,{context: {groupId:string} &  Partial<PolymerEntityInstanceInterface>;}>{
 
-    private readonly groupId:string;
-
-    private module:RcsbFvModulePublicInterface;
+    private readonly config:SequenceIdentityPfvManagerInterface<R>;
 
     constructor(config:SequenceIdentityPfvManagerInterface<R>) {
         super(config);
-        this.groupId = config.groupId;
+        this.config = config;
     }
 
     async create(): Promise<RcsbFvModulePublicInterface | undefined> {
-        this.module = await buildSequenceIdentityAlignmentFv(
+        const module:RcsbFvModulePublicInterface = await buildSequenceIdentityAlignmentFv(
             this.rcsbFvDivId,
-            this.groupId,
-            undefined,
+            this.config.groupId,
+            this.config.query,
             {
                 ... this.additionalConfig,
                 boardConfig: this.boardConfigContainer.get(),
@@ -82,18 +82,21 @@ class SequenceIdentityPfvManager<R> extends AbstractPfvManager<{groupId:string},
                             }
                         });
                     })
+                },
+                beforeChangeCallback: (module) => {
+                    this.config.pfvChangeCallback({context:{groupId:this.config.groupId}});
                 }
             }
         );
-        this.rcsbFvContainer.set(this.module);
+        this.rcsbFvContainer.set(module);
         await this.readyStateLoad();
-        return this.module;
+        return module;
     }
 
     private async readyStateLoad(): Promise<void> {
-        const alignments: AlignmentResponse = await this.module.getAlignmentResponse();
+        const alignments: AlignmentResponse = await this.rcsbFvContainer.get()!.getAlignmentResponse();
         if(alignments.target_alignment && alignments.target_alignment.length > 0 && typeof alignments.target_alignment[0]?.target_id === "string"){
-            this.loadAlignment({queryId:this.groupId}, alignments.target_alignment[0]);
+            this.loadAlignment({queryId:this.config.groupId}, alignments.target_alignment[0]);
         }
     }
 

+ 7 - 2
src/RcsbFvSequence/SequenceViews/RcsbView/PfvManagerFactoryImplementation/UniprotPfvManagerFactory.ts

@@ -7,7 +7,7 @@ import {
 import {
     RcsbFvModulePublicInterface
 } from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvModule/RcsbFvModuleInterface";
-import {buildUniprotFv, TagDelimiter} from "@rcsb/rcsb-saguaro-app";
+import {buildUniprotAlignmentFv, TagDelimiter} from "@rcsb/rcsb-saguaro-app";
 import {
     UniprotSequenceOnchangeInterface
 } from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvBuilder/RcsbFvUniprotBuilder";
@@ -17,9 +17,11 @@ import {
 import {AlignmentResponse, TargetAlignment} from "@rcsb/rcsb-api-tools/build/RcsbGraphQL/Types/Borrego/GqlTypes";
 import {MsaRowTitleComponent} from "./MsaPfvComponents/MsaRowTitleComponent";
 import {MsaRowMarkComponent} from "./MsaPfvComponents/MsaRowMarkComponent";
+import {SearchQuery} from "@rcsb/rcsb-api-tools/build/RcsbSearch/Types/SearchQueryInterface";
 
 interface UniprotPfvManagerInterface<R> extends PfvManagerFactoryConfigInterface<R,{context: UniprotSequenceOnchangeInterface;}> {
     upAcc:string;
+    query?: SearchQuery
 }
 
 export class UniprotPfvManagerFactory<R> implements PfvManagerFactoryInterface<{upAcc:string},R,{context: UniprotSequenceOnchangeInterface;}> {
@@ -40,18 +42,21 @@ type AlignmentDataType = {
 class UniprotPfvManager<R> extends AbstractPfvManager<{upAcc:string},R,{context: UniprotSequenceOnchangeInterface;}>{
 
     private readonly upAcc:string;
+    private readonly config:UniprotPfvManagerInterface<R>;
 
     private module:RcsbFvModulePublicInterface;
 
     constructor(config:UniprotPfvManagerInterface<R>) {
         super(config);
+        this.config = config;
         this.upAcc = config.upAcc;
     }
 
     async create(): Promise<RcsbFvModulePublicInterface | undefined> {
-        this.module = await buildUniprotFv(
+        this.module = await buildUniprotAlignmentFv(
             this.rcsbFvDivId,
             this.upAcc,
+            this.config.query,
             {
                 ... this.additionalConfig,
                 boardConfig: this.boardConfigContainer.get(),

+ 8 - 1
src/RcsbFvSequence/SequenceViews/RcsbView/RcsbView.tsx

@@ -126,8 +126,15 @@ export class RcsbView<T,R,U> extends AbstractView<RcsbViewInterface<T,R,U>, {},
         const width: number = window.document.getElementById(this.componentDivId)?.getBoundingClientRect().width ?? 0;
         const trackWidth: number = width - 190 - 55;
         this.boardConfigContainer.set({...this.boardConfigContainer.get(), trackWidth});
+        const select = this.rcsbFvContainer.get()?.getFv().getSelection("select");
+        const dom = this.rcsbFvContainer.get()?.getFv().getDomain();
         await this.rcsbFvContainer.get()?.getFv().updateBoardConfig({boardConfigData:{trackWidth:trackWidth}})
-        await this.structureSelectionCallback();
+        if(select)
+            this.rcsbFvContainer.get()?.getFv().setSelection({
+                elements: select.map(s=>({begin:s.rcsbFvTrackDataElement.begin, end:s.rcsbFvTrackDataElement.end })),
+                mode:"select"
+            });
+        if(dom) this.rcsbFvContainer.get()?.getFv().setDomain(dom);
         return void 0;
     }
 

+ 7 - 5
src/RcsbFvStructure/StructureViewerBehaviour/UniprotBehaviour.ts → src/RcsbFvStructure/StructureViewerBehaviour/MsaBehaviour.ts

@@ -23,7 +23,7 @@ import {RegionSelectionInterface} from "../../RcsbFvState/RcsbFvSelectorManager"
 import {defaultInputTarget} from "concurrently/dist/src/defaults";
 import {TargetAlignment} from "@rcsb/rcsb-api-tools/build/RcsbGraphQL/Types/Borrego/GqlTypes";
 
-export class UniprotBehaviourObserver<R> implements StructureViewerBehaviourObserverInterface<R> {
+export class MsaBehaviourObserver<R> implements StructureViewerBehaviourObserverInterface<R> {
 
     private structureBehaviour: StructureViewerBehaviourInterface;
     private readonly structureLoader: StructureLoaderInterface<[ViewerCallbackManagerInterface & ViewerActionManagerInterface <R>,{entryId:string;entityId:string;},TargetAlignment]>;
@@ -35,7 +35,7 @@ export class UniprotBehaviourObserver<R> implements StructureViewerBehaviourObse
         structureViewer: ViewerCallbackManagerInterface & ViewerActionManagerInterface<R>,
         stateManager: RcsbFvStateInterface
     ): void {
-        this.structureBehaviour = new UniprotBehaviour(structureViewer, stateManager, this.structureLoader);
+        this.structureBehaviour = new MsaBehaviour(structureViewer, stateManager, this.structureLoader);
     }
 
     public unsubscribe(): void {
@@ -52,7 +52,7 @@ type AlignmentDataType = {
     },
     targetAlignment: TargetAlignment;
 };
-class UniprotBehaviour<R> implements StructureViewerBehaviourInterface {
+class MsaBehaviour<R> implements StructureViewerBehaviourInterface {
 
     private readonly structureViewer: ViewerCallbackManagerInterface & ViewerActionManagerInterface<R>;
     private readonly stateManager: RcsbFvStateInterface;
@@ -100,6 +100,9 @@ class UniprotBehaviour<R> implements StructureViewerBehaviourInterface {
         await this.removeComponent();
         if(!data || data.length == 0)
             this.resetPluginView();
+        const numRes = data?.map(d=>(d.region.end-d.region.begin+1)).reduce((prev,curr)=>prev+curr,0);
+        if(!numRes)
+            return;
         data?.forEach(d=>{
             const {modelId, labelAsymId, region, operatorName} = d;
             const regions = [region];
@@ -117,8 +120,7 @@ class UniprotBehaviour<R> implements StructureViewerBehaviourInterface {
                     end: r.end,
                     operatorName
                 }));
-                const nRes = ranges.map(r=>r.end-r.begin+1).reduce((prev,curr)=>curr+prev,0);
-                if( nRes <= this.CREATE_COMPONENT_THR)
+                if( numRes <= this.CREATE_COMPONENT_THR)
                     asyncScheduler.schedule(async ()=>{
                         const x = residues[0];
                         const y = residues[residues.length-1];

+ 82 - 15
src/RcsbFvStructure/StructureViewers/MolstarViewer/TrajectoryPresetProvider/AlignmentRepresentationPresetProvider.ts

@@ -10,7 +10,8 @@ import {PluginContext} from "molstar/lib/mol-plugin/context";
 import {PluginStateObject} from "molstar/lib/mol-plugin-state/objects";
 import {StateObjectRef} from "molstar/lib/mol-state";
 import {
-    QueryContext,
+    Model,
+    QueryContext, ResidueIndex,
     Structure,
     StructureElement,
     StructureProperties as SP,
@@ -24,7 +25,7 @@ import {ColorTheme} from "molstar/lib/mol-theme/color";
 import {createSelectionExpressions} from "@rcsb/rcsb-molstar/build/src/viewer/helpers/selection";
 import {ParamDefinition as PD} from "molstar/lib/mol-util/param-definition";
 import {Loci} from "molstar/lib/mol-model/loci";
-import {alignAndSuperpose, superpose} from "molstar/lib/mol-model/structure/structure/util/superposition";
+import {superpose} from "molstar/lib/mol-model/structure/structure/util/superposition";
 import {Mat4} from "molstar/lib/mol-math/linear-algebra";
 import {SymmetryOperator} from "molstar/lib/mol-math/geometry/symmetry-operator";
 import {StateTransforms} from "molstar/lib/mol-plugin-state/transforms";
@@ -33,6 +34,11 @@ import {AlignedRegion, TargetAlignment} from "@rcsb/rcsb-api-tools/build/RcsbGra
 import {AlignmentMapper as AM} from "../../../../Utils/AlignmentMapper";
 import {compile} from 'molstar/lib/mol-script/runtime/query/compiler';
 import reprBuilder = StructureRepresentationPresetProvider.reprBuilder;
+import {
+    QualityAssessment,
+} from "molstar/lib/extensions/model-archive/quality-assessment/prop";
+import {MmcifFormat} from "molstar/lib/mol-model-formats/structure/mmcif";
+import {CustomProperty} from "molstar/lib/mol-model-props/common/custom-property";
 
 let refData: Structure|undefined = undefined;
 let refParams: StructureAlignmentParamsType|undefined = undefined;
@@ -202,28 +208,30 @@ async function structuralAlignment(plugin: PluginContext, ref:StructureAlignment
     }else{
         const pdbResIndexes: number[] = [];
         const refResIndexes: number[] = [];
-        if(ref.targetAlignment?.aligned_regions && pdb.targetAlignment?.aligned_regions){
+        const pdbData: Structure = structure;
+        const pdbUnit:{unit: Unit; q:CustomProperty.Data<QualityAssessment>}|undefined = await findFirstInstanceUnit(pdbData,pdb.labelAsymId);
+        const refUnit:{unit: Unit; q:CustomProperty.Data<QualityAssessment>}|undefined =  refData ? await findFirstInstanceUnit(refData, ref.labelAsymId) : undefined;
+        if( pdbUnit && refUnit && ref.targetAlignment?.aligned_regions && pdb.targetAlignment?.aligned_regions){
             const alignmentList = AM.getAllTargetIntersections( ref.targetAlignment.aligned_regions as AlignedRegion[], pdb.targetAlignment.aligned_regions as AlignedRegion[])
             alignmentList.forEach(alignment=>{
                 const refRange = AM.range(alignment[0].target_begin, alignment[0].target_end);
                 const pdbRange = AM.range(alignment[1].target_begin, alignment[1].target_end);
                 refRange.forEach((refIndex,n)=>{
                     const pdbIndex = pdbRange[n];
-                    const pdbLoci =  residueToLoci(pdb, pdbIndex, structure);
+                    const pdbLoci =  residueToLoci(pdb, pdbIndex, pdbData);
                     const refLoci =  residueToLoci(refParams!, refIndex, refData!);
-                    if(!Loci.isEmpty(pdbLoci) && !Loci.isEmpty(refLoci)){
+                    if(!Loci.isEmpty(pdbLoci) && !Loci.isEmpty(refLoci) && checkLocalScore(pdbUnit.q.value.pLDDT, pdbIndex) && checkLocalScore(refUnit.q.value.pLDDT, refIndex)){
                         pdbResIndexes.push(pdbIndex)
                         refResIndexes.push(refIndex)
                     }
                 });
             })
         }
-        const pdbData: Structure = structure;
-        const pdbUnit:Unit|undefined = findFirstInstanceUnit(pdbData,pdb.labelAsymId);
-        const refUnit:Unit|undefined =  refData ? findFirstInstanceUnit(refData, ref.labelAsymId) : undefined;
         if(pdbData && pdbUnit && refData && refUnit){
             const refLoci: Loci = residueListToLoci(refParams!, refResIndexes, refData);
-            const pdbLoci: Loci = residueListToLoci(pdb, pdbResIndexes, structure);
+            const pdbLoci: Loci = residueListToLoci(pdb, pdbResIndexes, pdbData);
+            console.log(refLoci);
+            console.log(pdbLoci);
             if(StructureElement.Loci.is(refLoci) && StructureElement.Loci.is(pdbLoci)) {
                 const pivot = plugin.managers.structure.hierarchy.findStructure(refLoci.structure);
                 const coordinateSystem = pivot?.transform?.cell.obj?.data.coordinateSystem;
@@ -235,14 +243,69 @@ async function structuralAlignment(plugin: PluginContext, ref:StructureAlignment
     }
 }
 
-function findFirstInstanceUnit(structure: Structure, labelAsymId: string): Unit|undefined {
+async function findFirstInstanceUnit(structure: Structure, labelAsymId: string): Promise<{unit: Unit; q:CustomProperty.Data<QualityAssessment>}|undefined> {
     const l = StructureElement.Location.create(structure);
     for(const unit of structure.units) {
         StructureElement.Location.set(l, structure, unit, unit.elements[0]);
         if (SP.chain.label_asym_id(l) == labelAsymId) {
-            return unit;
+            const q:CustomProperty.Data<QualityAssessment> = await obtain(unit.model);
+            return {unit,q};
+        }
+    }
+}
+
+function checkLocalScore(scoreMap: Map<ResidueIndex, number>|undefined, index: number): boolean{
+    if(typeof  scoreMap == "undefined")
+        return true;
+    return !!(scoreMap.get(index as ResidueIndex) && scoreMap.get(index as ResidueIndex)! >= 70);
+
+}
+
+const Empty = {
+    value: {
+        localMetrics: new Map()
+    }
+};
+async function obtain(model: Model): Promise<CustomProperty.Data<QualityAssessment>> {
+    if (!model || !MmcifFormat.is(model.sourceData)) return Empty;
+    const { ma_qa_metric, ma_qa_metric_local } = model.sourceData.data.db;
+    const { model_id, label_asym_id, label_seq_id, metric_id, metric_value } = ma_qa_metric_local;
+    const { index } = model.atomicHierarchy;
+
+    // for simplicity we assume names in ma_qa_metric for mode 'local' are unique
+    const localMetrics = new Map<string, Map<ResidueIndex, number>>();
+    const localNames = new Map<number, string>();
+
+    for (let i = 0, il = ma_qa_metric._rowCount; i < il; i++) {
+        if (ma_qa_metric.mode.value(i) !== 'local') continue;
+
+        const name = ma_qa_metric.name.value(i);
+        if (localMetrics.has(name)) {
+            console.warn(`local ma_qa_metric with name '${name}' already added`);
+            continue;
         }
+
+        localMetrics.set(name, new Map());
+        localNames.set(ma_qa_metric.id.value(i), name);
     }
+
+    for (let i = 0, il = ma_qa_metric_local._rowCount; i < il; i++) {
+        if (model_id.value(i) !== model.modelNum) continue;
+
+        const labelAsymId = label_asym_id.value(i);
+        const entityIndex = index.findEntity(labelAsymId);
+        const rI = index.findResidue(model.entities.data.id.value(entityIndex), labelAsymId, label_seq_id.value(i));
+        const name = localNames.get(metric_id.value(i))!;
+        localMetrics.get(name)!.set(rI, metric_value.value(i));
+    }
+
+    return {
+        value: {
+            localMetrics,
+            pLDDT: localMetrics.get('pLDDT'),
+            qmean: localMetrics.get('qmean'),
+        }
+    };
 }
 
 const SuperpositionTag = 'SuperpositionTransform';
@@ -272,9 +335,11 @@ function residueToLoci(pdb:StructureAlignmentParamsType, pdbIndex:number, struct
     const expression = MS.struct.generator.atomGroups({
         'chain-test': MS.core.logic.and([
             MS.core.rel.eq([MS.ammp('label_asym_id'), pdb.labelAsymId]),
-            MS.core.rel.eq([MS.acp('operatorName'), pdb.operatorName])
+            MS.core.rel.eq([MS.acp('operatorName'), pdb.operatorName]),
+            MS.core.rel.eq([MS.acp('modelIndex'),1])
         ]),
-        'residue-test':MS.core.rel.eq([MS.ammp('label_seq_id'), pdbIndex])
+        'residue-test':MS.core.rel.eq([MS.ammp('label_seq_id'), pdbIndex]),
+        'atom-test':MS.core.rel.eq([MS.ammp("label_atom_id"),"CA"])
     });
     const query = compile<StructureSelection>(expression);
     const selection = query(new QueryContext(structure));
@@ -285,11 +350,13 @@ function residueListToLoci(pdb:StructureAlignmentParamsType, indexList:number[],
     const expression = MS.struct.generator.atomGroups({
         'chain-test': MS.core.logic.and([
             MS.core.rel.eq([MS.ammp('label_asym_id'), pdb.labelAsymId]),
-            MS.core.rel.eq([MS.acp('operatorName'), pdb.operatorName])
+            MS.core.rel.eq([MS.acp('operatorName'), pdb.operatorName]),
+            MS.core.rel.eq([MS.acp('modelIndex'),1])
         ]),
         'residue-test':MS.core.logic.or(
             indexList.map(index=>MS.core.rel.eq([MS.ammp('label_seq_id'), index]))
-        )
+        ),
+        'atom-test':MS.core.rel.eq([MS.ammp("label_atom_id"),"CA"])
     });
     const query = compile<StructureSelection>(expression);
     const selection = query(new QueryContext(structure));

+ 2 - 1
src/RcsbSaguaro3D.js

@@ -1,8 +1,9 @@
 import {RcsbFv3DCustom as custom} from "./RcsbFv3D/RcsbFv3DCustom";
 import {RcsbFv3DAssembly as assembly} from "./RcsbFv3D/RcsbFv3DAssembly";
 import {RcsbFv3DUniprot as uniprot} from "./RcsbFv3D/RcsbFv3DUniprot";
+import {RcsbFv3DSequenceIdentityInterface as sequenceIdentity} from "./RcsbFv3D/RcsbFv3DSequenceIdentity";
 
-export {custom, assembly, uniprot};
+export {custom, assembly, uniprot, sequenceIdentity};
 
 export {
     RcsbRequestContextManager

+ 3 - 3
src/examples/sequence-identity/index.ts

@@ -5,9 +5,9 @@ document.addEventListener("DOMContentLoaded", function(event) {
 
     const panel3d = new RcsbFv3DSequenceIdentity({
         config:{
-            groupId:"1_30",
-            title: "Title >> Sequence Identity 1_30",
-            subtitle: "Subtitle >> Sequence Identity 1_30",
+            groupId:"5_30",
+            title: "Title >> Sequence Identity 5_30",
+            subtitle: "Subtitle >> Sequence Identity 5_30",
         }
     });
     panel3d.render();