*
* @param sf
*/
- public abstract boolean add(SequenceFeature sf);
+ abstract boolean add(SequenceFeature sf);
/**
* Returns a (possibly empty) list of features, optionally restricted to
* @param type
* @return
*/
- public abstract List<SequenceFeature> findFeatures(int from, int to,
+ abstract List<SequenceFeature> findFeatures(int from, int to,
String... type);
/**
* @param type
* @return
*/
- public abstract List<SequenceFeature> getAllFeatures(String... type);
+ abstract List<SequenceFeature> getAllFeatures(String... type);
- public abstract int getFeatureCount(boolean positional, String... type);
+ /**
+ * Answers the number of (positional or non-positional) features, optionally
+ * restricted to specified feature types. Contact features are counted as 1.
+ *
+ * @param positional
+ * @param type
+ * @return
+ */
+ abstract int getFeatureCount(boolean positional, String... type);
+
+ /**
+ * Answers the total length of positional features, optionally restricted to
+ * specified feature types. Contact features are counted as length 1.
+ *
+ * @param type
+ * @return
+ */
+ abstract int getTotalFeatureLength(String... type);
/**
* Answers a list of all positional features, optionally restricted to
* @param type
* @return
*/
- public abstract List<SequenceFeature> getPositionalFeatures(
+ abstract List<SequenceFeature> getPositionalFeatures(
String... type);
/**
*
* @return
*/
- public abstract List<SequenceFeature> getContactFeatures(String... type);
+ abstract List<SequenceFeature> getContactFeatures(String... type);
/**
* Answers a list of all non-positional features, optionally restricted to
* if no type is specified, all are returned
* @return
*/
- public abstract List<SequenceFeature> getNonPositionalFeatures(
+ abstract List<SequenceFeature> getNonPositionalFeatures(
String... type);
/**
*
* @param sf
*/
- public abstract boolean delete(SequenceFeature sf);
+ abstract boolean delete(SequenceFeature sf);
/**
* Answers true if this store contains at least one feature, else false
*
* @return
*/
- public abstract boolean hasFeatures();
+ abstract boolean hasFeatures();
/**
* Returns a set of the distinct feature groups present in the collection. The
* @param type
* @return
*/
- public abstract Set<String> getFeatureGroups(boolean positionalFeatures,
+ abstract Set<String> getFeatureGroups(boolean positionalFeatures,
String... type);
/**
* @param groups
* @return
*/
- public abstract Set<String> getFeatureTypesForGroups(
+ abstract Set<String> getFeatureTypesForGroups(
boolean positionalFeatures, String... groups);
/**
*
* @return
*/
- public abstract Set<String> getFeatureTypes();
+ abstract Set<String> getFeatureTypes();
}
\ No newline at end of file
import jalview.api.FeatureSettingsControllerI;
import jalview.bin.Cache;
import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceI;
import jalview.gui.Help.HelpId;
import jalview.io.JalviewFileChooser;
private boolean handlingUpdate = false;
/**
- * contains a float[3] for each feature type string. created by setTableData
+ * holds {featureCount, totalExtent} for each feature type
*/
Map<String, float[]> typeWidth = null;
typeWidth = new Hashtable<String, float[]>();
// TODO: change avWidth calculation to 'per-sequence' average and use long
// rather than float
- float[] avWidth = null;
- SequenceFeature[] tmpfeatures;
- String group = null, type;
- Vector<String> visibleChecks = new Vector<String>();
-
- // Find out which features should be visible depending on which groups
- // are selected / deselected
- // and recompute average width ordering
+
+ Set<String> displayableTypes = new HashSet<String>();
+
+ /*
+ * determine which feature types may be visible depending on
+ * which groups are selected, and recompute average width data
+ */
for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
{
SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
- tmpfeatures = seq.getSequenceFeatures();
- if (tmpfeatures == null)
- {
- continue;
- }
- Set<String> types = seq.getFeatures().getFeatureTypes();
- int index = 0;
- while (index < tmpfeatures.length)
- {
- group = tmpfeatures[index].featureGroup;
-
- if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0)
- {
- index++;
- continue;
- }
+ /*
+ * get the sequence's groups for positional features
+ * and keep track of which groups are visible
+ */
+ Set<String> groups = seq.getFeatures().getFeatureGroups(true);
+ Set<String> visibleGroups = new HashSet<String>();
+ for (String group : groups)
+ {
if (group == null || checkGroupState(group))
{
- type = tmpfeatures[index].getType();
- if (!visibleChecks.contains(type))
- {
- visibleChecks.addElement(type);
- }
- }
- if (!typeWidth.containsKey(tmpfeatures[index].getType()))
- {
- typeWidth.put(tmpfeatures[index].getType(),
- avWidth = new float[3]);
- }
- else
- {
- avWidth = typeWidth.get(tmpfeatures[index].getType());
- }
- avWidth[0]++;
- if (tmpfeatures[index].getBegin() > tmpfeatures[index].getEnd())
- {
- avWidth[1] += 1 + tmpfeatures[index].getBegin()
- - tmpfeatures[index].getEnd();
+ visibleGroups.add(group);
}
- else
+ }
+
+ /*
+ * get distinct feature types for visible groups
+ * record distinct visible types, and their count and total length
+ */
+ Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
+ visibleGroups.toArray(new String[visibleGroups.size()]));
+ for (String type : types)
+ {
+ displayableTypes.add(type);
+ float[] avWidth = typeWidth.get(type);
+ if (avWidth == null)
{
- avWidth[1] += 1 + tmpfeatures[index].getEnd()
- - tmpfeatures[index].getBegin();
+ avWidth = new float[2];
+ typeWidth.put(type, avWidth);
}
- index++;
+ // todo this could include features with a non-visible group
+ // - do we greatly care?
+ // todo should we include non-displayable features here, and only
+ // update when features are added?
+ avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
+ avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
}
}
- int fSize = visibleChecks.size();
- Object[][] data = new Object[fSize][3];
+ Object[][] data = new Object[displayableTypes.size()][3];
int dataIndex = 0;
if (fr.hasRenderOrder())
List<String> frl = fr.getRenderOrder();
for (int ro = frl.size() - 1; ro > -1; ro--)
{
- type = frl.get(ro);
+ String type = frl.get(ro);
- if (!visibleChecks.contains(type))
+ if (!displayableTypes.contains(type))
{
continue;
}
data[dataIndex][2] = new Boolean(af.getViewport()
.getFeaturesDisplayed().isVisible(type));
dataIndex++;
- visibleChecks.removeElement(type);
+ displayableTypes.remove(type);
}
}
- fSize = visibleChecks.size();
- for (int i = 0; i < fSize; i++)
+ /*
+ * process any extra features belonging only to
+ * a group which was just selected
+ */
+ while (!displayableTypes.isEmpty())
{
- // These must be extra features belonging to the group
- // which was just selected
- type = visibleChecks.elementAt(i).toString();
+ String type = displayableTypes.iterator().next();
data[dataIndex][0] = type;
data[dataIndex][1] = fr.getFeatureStyle(type);
data[dataIndex][2] = new Boolean(true);
dataIndex++;
+ displayableTypes.remove(type);
}
if (originalData == null)
assertEquals(features.size(), 3);
assertFalse(features.contains(sf3));
}
+
+ @Test(groups = "Functional")
+ public void testGetTotalFeatureLength()
+ {
+ SequenceFeaturesI store = new SequenceFeatures();
+ assertEquals(store.getTotalFeatureLength(), 0);
+
+ SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
+ Float.NaN, null);
+ assertTrue(store.add(sf1));
+ assertEquals(store.getTotalFeatureLength(), 11);
+
+ // re-add does nothing!
+ assertFalse(store.add(sf1));
+ assertEquals(store.getTotalFeatureLength(), 11);
+
+ /*
+ * add non-positional feature
+ */
+ SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 0, 0,
+ Float.NaN, null);
+ store.add(sf3);
+ assertEquals(store.getTotalFeatureLength(), 11);
+
+ /*
+ * add contact feature - counts 1 to feature length
+ */
+ SequenceFeature sf4 = new SequenceFeature("Disulphide Bond", "desc",
+ 10, 20, Float.NaN, null);
+ store.add(sf4);
+ assertEquals(store.getTotalFeatureLength(), 12);
+
+ /*
+ * add another Pfam
+ */
+ SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 10, 20,
+ Float.NaN, null);
+ store.add(sf5);
+ assertEquals(store.getTotalFeatureLength(), 23);
+
+ /*
+ * delete features
+ */
+ assertTrue(store.delete(sf3)); // non-positional
+ assertEquals(store.getTotalFeatureLength(), 23); // no change
+
+ assertTrue(store.delete(sf5));
+ assertEquals(store.getTotalFeatureLength(), 12);
+
+ assertTrue(store.delete(sf4)); // contact
+ assertEquals(store.getTotalFeatureLength(), 11);
+
+ assertTrue(store.delete(sf1));
+ assertEquals(store.getTotalFeatureLength(), 0);
+ }
}