+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<SequenceFeature> findFeaturesAtResidue(SequenceI sequence,
+ int resNo)
+ {
+ List<SequenceFeature> result = new ArrayList<>();
+ if (!av.areFeaturesDisplayed() || getFeaturesDisplayed() == null)
+ {
+ return result;
+ }
+
+ /*
+ * include features at the position provided their feature type is
+ * displayed, and feature group is null or the empty string
+ * or marked for display
+ */
+ Set<String> visibleFeatures = getFeaturesDisplayed()
+ .getVisibleFeatures();
+ String[] visibleTypes = visibleFeatures
+ .toArray(new String[visibleFeatures.size()]);
+ List<SequenceFeature> features = sequence.getFeatures().findFeatures(
+ resNo, resNo, visibleTypes);
+
+ for (SequenceFeature sf : features)
+ {
+ if (!featureGroupNotShown(sf) && getColour(sf) != null)
+ {
+ result.add(sf);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Removes from the list of features any that duplicate the location of a
+ * feature of the same type. Should be used only for features of the same,
+ * simple, feature colour (which normally implies the same feature type). Does
+ * not check visibility settings for feature type or feature group.
+ *
+ * @param features
+ */
+ public void filterFeaturesForDisplay(List<SequenceFeature> features)
+ {
+ if (features.isEmpty())
+ {
+ return;
+ }
+ SequenceFeatures.sortFeatures(features, true);
+ SequenceFeature lastFeature = null;
+
+ Iterator<SequenceFeature> it = features.iterator();
+ while (it.hasNext())
+ {
+ SequenceFeature sf = it.next();
+
+ /*
+ * a feature is redundant for rendering purposes if it has the
+ * same extent as another (so would just redraw the same colour);
+ * (checking type and isContactFeature as a fail-safe here, although
+ * currently they are guaranteed to match in this context)
+ */
+ if (lastFeature != null && sf.getBegin() == lastFeature.getBegin()
+ && sf.getEnd() == lastFeature.getEnd()
+ && sf.isContactFeature() == lastFeature.isContactFeature()
+ && sf.getType().equals(lastFeature.getType()))
+ {
+ it.remove();
+ }
+ lastFeature = sf;
+ }
+ }
+
+ @Override
+ public Map<String, FeatureMatcherSetI> getFeatureFilters()
+ {
+ return featureFilters;
+ }
+
+ @Override
+ public void setFeatureFilters(Map<String, FeatureMatcherSetI> filters)
+ {
+ featureFilters = filters;
+ }
+
+ @Override
+ public FeatureMatcherSetI getFeatureFilter(String featureType)
+ {
+ return featureFilters.get(featureType);
+ }
+
+ @Override
+ public void setFeatureFilter(String featureType, FeatureMatcherSetI filter)
+ {
+ if (filter == null || filter.isEmpty())
+ {
+ featureFilters.remove(featureType);
+ }
+ else
+ {
+ featureFilters.put(featureType, filter);
+ }
+ }
+
+ /**
+ * Answers the colour for the feature, or null if the feature is excluded by
+ * feature group visibility, by filters, or by colour threshold settings. This
+ * method does not take feature visibility into account.
+ *
+ * @param sf
+ * @param fc
+ * @return
+ */
+ public Color getColor(SequenceFeature sf, FeatureColourI fc)
+ {
+ /*
+ * is the feature group displayed?
+ */
+ if (featureGroupNotShown(sf))
+ {
+ return null;
+ }
+
+ /*
+ * does the feature pass filters?
+ */
+ if (!featureMatchesFilters(sf))
+ {
+ return null;
+ }
+
+ return fc.getColor(sf);
+ }
+
+ /**
+ * Answers true if there no are filters defined for the feature type, or this
+ * feature matches the filters. Answers false if the feature fails to match
+ * filters.
+ *
+ * @param sf
+ * @return
+ */
+ protected boolean featureMatchesFilters(SequenceFeature sf)
+ {
+ FeatureMatcherSetI filter = featureFilters.get(sf.getType());
+ return filter == null ? true : filter.matches(sf);
+ }
+