2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.gui.structurechooser;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.BitSet;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.Comparator;
29 import java.util.List;
30 import java.util.Locale;
32 import jalview.datamodel.SequenceI;
33 import jalview.fts.api.FTSData;
34 import jalview.fts.core.FTSRestRequest;
36 public class TDBResultAnalyser
40 * model categories - update as needed. warnings output if unknown types
43 * Order denotes 'trust'
45 private static List<String> EXP_CATEGORIES = Arrays
47 { "EXPERIMENTALLY DETERMINED", "DEEP-LEARNING", "AB-INITIO",
48 "TEMPLATE-BASED", "CONFORMATIONAL ENSEMBLE" });
50 private SequenceI seq;
52 private Collection<FTSData> collectedResults;
54 private FTSRestRequest lastTdbRequest;
62 private int idx_mqual;
64 private int idx_mqualtype;
66 private int idx_resol;
71 private String filter = null;
74 * limit to particular source
76 private String sourceFilter = null;
78 private int idx_mprov;
80 public TDBResultAnalyser(SequenceI seq,
81 Collection<FTSData> collectedResults,
82 FTSRestRequest lastTdbRequest, String fieldToFilterBy,
86 this.collectedResults = collectedResults;
87 this.lastTdbRequest = lastTdbRequest;
88 this.filter = fieldToFilterBy;
89 this.sourceFilter = string;
90 idx_ups = lastTdbRequest.getFieldIndex("Uniprot Start");
91 idx_upe = lastTdbRequest.getFieldIndex("Uniprot End");
92 idx_mcat = lastTdbRequest.getFieldIndex("Model Category");
93 idx_mprov = lastTdbRequest.getFieldIndex("Provider");
94 idx_mqual = lastTdbRequest.getFieldIndex("Confidence");
95 idx_resol = lastTdbRequest.getFieldIndex("Resolution");
96 idx_mqualtype = lastTdbRequest.getFieldIndex("Confidence Score Type");
100 * maintain and resolve categories to 'trust order' TODO: change the trust
101 * scheme to something comprehensible.
104 * @return 0 for null cat, less than zero for others
106 public final int scoreCategory(String cat)
112 String upper_cat = cat.toUpperCase(Locale.ROOT);
113 int idx = EXP_CATEGORIES.indexOf(upper_cat);
116 System.out.println("Unknown category: '" + cat + "'");
117 EXP_CATEGORIES.add(upper_cat);
118 idx = EXP_CATEGORIES.size() - 1;
120 return -EXP_CATEGORIES.size() - idx;
124 * sorts records discovered by 3D beacons and excludes any that don't
125 * intersect with the sequence's start/end rage
129 public List<FTSData> getFilteredResponse()
131 List<FTSData> filteredResponse = new ArrayList<FTSData>();
133 // ignore anything outside the sequence region
134 for (FTSData row : collectedResults)
136 if (row.getSummaryData() != null
137 && row.getSummaryData()[idx_ups] != null)
139 int up_s = (Integer) row.getSummaryData()[idx_ups];
140 int up_e = (Integer) row.getSummaryData()[idx_upe];
141 String provider = (String) row.getSummaryData()[idx_mprov];
142 String mcat = (String) row.getSummaryData()[idx_mcat];
143 // this makes sure all new categories are in the score array.
144 int scorecat = scoreCategory(mcat);
145 if (sourceFilter == null || sourceFilter.equals(provider))
147 if (seq == row.getSummaryData()[0] && up_e > seq.getStart()
148 && up_s < seq.getEnd())
150 filteredResponse.add(row);
155 // sort according to decreasing length,
157 Collections.sort(filteredResponse, new Comparator<FTSData>()
160 public int compare(FTSData o1, FTSData o2)
162 Object[] o1data = o1.getSummaryData();
163 Object[] o2data = o2.getSummaryData();
164 int o1_s = (Integer) o1data[idx_ups];
165 int o1_e = (Integer) o1data[idx_upe];
166 int o1_cat = scoreCategory((String) o1data[idx_mcat]);
167 String o1_prov = ((String) o1data[idx_mprov])
168 .toUpperCase(Locale.ROOT);
169 int o2_s = (Integer) o2data[idx_ups];
170 int o2_e = (Integer) o2data[idx_upe];
171 int o2_cat = scoreCategory((String) o2data[idx_mcat]);
172 String o2_prov = ((String) o2data[idx_mprov])
173 .toUpperCase(Locale.ROOT);
174 String o1_qualtype = (String) o1data[idx_mqualtype],
175 o2_qualtype = (String) o2data[idx_mqualtype];
177 if (o1_cat == o2_cat)
181 int o1_xtent = o1_e - o1_s;
182 int o2_xtent = o2_e - o2_s;
183 if (o1_xtent == o2_xtent)
185 // EXPERIMENTAL DATA ALWAYS TRUMPS MODELS
186 if (o1_cat == scoreCategory(EXP_CATEGORIES.get(0)))
188 if (o1_prov.equals(o2_prov))
190 if ("PDBE".equals(o1_prov))
192 if (eitherNull(idx_resol, o1data, o2data))
194 return nonNullFirst(idx_resol, o1data, o2data);
196 // experimental structures, so rank on quality
197 double o1_res = (Double) o1data[idx_resol];
198 double o2_res = (Double) o2data[idx_resol];
199 return (o2_res < o1_res) ? 1
200 : (o2_res == o1_res) ? 0 : -1;
204 return 0; // no change in order
209 // PDBe always ranked above all other experimentally
210 // determined categories
211 return "PDBE".equals(o1_prov) ? -1
212 : "PDBE".equals(o2_prov) ? 1 : 0;
217 // RANK ON QUALITY - DOWNRANK THOSE WITH NO QUALITY MEASURE
218 if (eitherNull(idx_mqualtype, o1data, o2data))
220 return nonNullFirst(idx_mqualtype, o1data, o2data);
222 // ONLY COMPARE LIKE QUALITY SCORES
223 if (!o1_qualtype.equals(o2_qualtype))
225 // prefer LDDT measure over others
226 return "pLDDT".equals(o1_prov) ? -1
227 : "pLDDT".equals(o2_prov) ? 1 : 0;
229 // OR NO VALUE FOR THE QUALITY
230 if (eitherNull(idx_mqual, o1data, o2data))
232 return nonNullFirst(idx_mqual, o1data, o2data);
234 // models, so rank on qmean - b
235 double o1_mq = (Double) o1data[idx_mqual];
236 double o2_mq = (Double) o2data[idx_mqual];
237 return (o2_mq < o1_mq) ? 1 : (o2_mq == o1_mq) ? 0 : -1;
242 return o1_xtent - o2_xtent;
252 // if both are not experimental, then favour alphafold
253 if (o2_cat > 0 && o1_cat > 0)
255 return "ALPHAFOLD DB".equals(o1_prov) ? -1
256 : "ALPHAFOLD DB".equals(o2_prov) ? 1 : 0;
258 return o2_cat - o1_cat;
262 private int nonNullFirst(int idx_resol, Object[] o1data,
265 return o1data[idx_resol] == o2data[idx_resol] ? 0
266 : o1data[idx_resol] != null ? -1 : 1;
269 private boolean eitherNull(int idx_resol, Object[] o1data,
272 return (o1data[idx_resol] == null || o2data[idx_resol] == null);
276 public boolean equals(Object obj)
278 return super.equals(obj);
281 return filteredResponse;
285 * return list of structures to be marked as selected for this sequence
286 * according to given criteria
288 * @param filteredStructures
289 * - sorted, filtered structures from getFilteredResponse
292 public List<FTSData> selectStructures(List<FTSData> filteredStructures)
294 List<FTSData> selected = new ArrayList<FTSData>();
295 BitSet cover = new BitSet();
296 cover.set(seq.getStart(), seq.getEnd());
297 // walk down the list of structures, selecting some to add to selected
298 // TODO: could do simple DP - double loop to select largest number of
299 // structures covering largest number of sites
300 for (FTSData structure : filteredStructures)
302 Object[] odata = structure.getSummaryData();
303 int o1_s = (Integer) odata[idx_ups];
304 int o1_e = (Integer) odata[idx_upe];
305 int o1_cat = scoreCategory((String) odata[idx_mcat]);
306 BitSet scover = new BitSet();
307 // measure intersection
308 scover.set(o1_s, o1_e);
310 if (scover.cardinality() > 4)
312 selected.add(structure);
313 // clear the range covered by this structure
314 cover.andNot(scover);
317 if (selected.size() == 0)
321 // final step is to sort on length - this might help the superposition
323 Collections.sort(selected, new Comparator<FTSData>()
326 public int compare(FTSData o1, FTSData o2)
328 Object[] o1data = o1.getSummaryData();
329 Object[] o2data = o2.getSummaryData();
330 int o1_xt = ((Integer) o1data[idx_upe])
331 - ((Integer) o1data[idx_ups]);
332 int o1_cat = scoreCategory((String) o1data[idx_mcat]);
333 int o2_xt = ((Integer) o2data[idx_upe] - (Integer) o2data[idx_ups]);
334 int o2_cat = scoreCategory((String) o2data[idx_mcat]);
335 return o2_xt - o1_xt;
339 ThreeDBStructureChooserQuerySource.FILTER_FIRST_BEST_COVERAGE))
341 return selected.subList(0, 1);