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.viewmodel.seqfeatures;
23 import java.awt.Color;
24 import java.beans.PropertyChangeListener;
25 import java.beans.PropertyChangeSupport;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Comparator;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.Hashtable;
32 import java.util.Iterator;
33 import java.util.List;
36 import java.util.concurrent.ConcurrentHashMap;
38 import jalview.api.AlignViewportI;
39 import jalview.api.AlignmentViewPanel;
40 import jalview.api.FeatureColourI;
41 import jalview.api.FeaturesDisplayedI;
42 import jalview.datamodel.AlignedCodonFrame;
43 import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping;
44 import jalview.datamodel.AlignmentI;
45 import jalview.datamodel.MappedFeatures;
46 import jalview.datamodel.SearchResultMatchI;
47 import jalview.datamodel.SearchResults;
48 import jalview.datamodel.SearchResultsI;
49 import jalview.datamodel.SequenceFeature;
50 import jalview.datamodel.SequenceI;
51 import jalview.datamodel.features.FeatureMatcherSetI;
52 import jalview.datamodel.features.SequenceFeatures;
53 import jalview.renderer.seqfeatures.FeatureRenderer;
54 import jalview.schemes.FeatureColour;
55 import jalview.util.ColorUtils;
56 import jalview.util.Platform;
58 public abstract class FeatureRendererModel
59 implements jalview.api.FeatureRenderer
62 * a data bean to hold one row of feature settings from the gui
64 public static class FeatureSettingsBean
66 public final String featureType;
68 public final FeatureColourI featureColour;
70 public final FeatureMatcherSetI filter;
72 public final Boolean show;
74 public FeatureSettingsBean(String type, FeatureColourI colour,
75 FeatureMatcherSetI theFilter, Boolean isShown)
78 featureColour = colour;
85 * global transparency for feature
87 protected float transparency = 1.0f;
90 * colour scheme for each feature type
92 protected Map<String, FeatureColourI> featureColours = new ConcurrentHashMap<>();
95 * visibility flag for each feature group
97 protected Map<String, Boolean> featureGroups = new ConcurrentHashMap<>();
100 * filters for each feature type
102 protected Map<String, FeatureMatcherSetI> featureFilters = new HashMap<>();
104 protected String[] renderOrder;
106 Map<String, Float> featureOrder = null;
108 protected AlignViewportI av;
110 private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
114 public AlignViewportI getViewport()
119 public FeatureRendererSettings getSettings()
121 return new FeatureRendererSettings(this);
124 public void transferSettings(FeatureRendererSettings fr)
126 this.renderOrder = fr.renderOrder;
127 this.featureGroups = fr.featureGroups;
128 this.featureColours = fr.featureColours;
129 this.transparency = fr.transparency;
130 this.featureOrder = fr.featureOrder;
134 * update from another feature renderer
139 public void transferSettings(jalview.api.FeatureRenderer _fr)
141 FeatureRenderer fr = (FeatureRenderer) _fr;
142 FeatureRendererSettings frs = new FeatureRendererSettings(fr);
143 this.renderOrder = frs.renderOrder;
144 this.featureGroups = frs.featureGroups;
145 this.featureColours = frs.featureColours;
146 this.featureFilters = frs.featureFilters;
147 this.transparency = frs.transparency;
148 this.featureOrder = frs.featureOrder;
149 if (av != null && av != fr.getViewport())
151 // copy over the displayed feature settings
152 if (_fr.getFeaturesDisplayed() != null)
154 FeaturesDisplayedI fd = getFeaturesDisplayed();
157 setFeaturesDisplayedFrom(_fr.getFeaturesDisplayed());
164 for (String type : _fr.getFeaturesDisplayed()
165 .getVisibleFeatures())
175 public void setFeaturesDisplayedFrom(FeaturesDisplayedI featuresDisplayed)
177 av.setFeaturesDisplayed(new FeaturesDisplayed(featuresDisplayed));
181 public void setVisible(String featureType)
183 FeaturesDisplayedI fdi = av.getFeaturesDisplayed();
186 av.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
188 if (!fdi.isRegistered(featureType))
190 pushFeatureType(Arrays.asList(new String[] { featureType }));
192 fdi.setVisible(featureType);
196 public void setAllVisible(List<String> featureTypes)
198 FeaturesDisplayedI fdi = av.getFeaturesDisplayed();
201 av.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
203 List<String> nft = new ArrayList<>();
204 for (String featureType : featureTypes)
206 if (!fdi.isRegistered(featureType))
208 nft.add(featureType);
213 pushFeatureType(nft);
215 fdi.setAllVisible(featureTypes);
219 * push a set of new types onto the render order stack. Note - this is a
220 * direct mechanism rather than the one employed in updateRenderOrder
224 private void pushFeatureType(List<String> types)
227 int ts = types.size();
228 String neworder[] = new String[(renderOrder == null ? 0
229 : renderOrder.length) + ts];
230 types.toArray(neworder);
231 if (renderOrder != null)
233 System.arraycopy(neworder, 0, neworder, renderOrder.length, ts);
234 System.arraycopy(renderOrder, 0, neworder, 0, renderOrder.length);
236 renderOrder = neworder;
239 protected Map<String, float[][]> minmax = new Hashtable<>();
241 public Map<String, float[][]> getMinMax()
247 * normalise a score against the max/min bounds for the feature type.
249 * @param sequenceFeature
250 * @return byte[] { signed, normalised signed (-127 to 127) or unsigned
253 protected final byte[] normaliseScore(SequenceFeature sequenceFeature)
255 float[] mm = minmax.get(sequenceFeature.type)[0];
256 final byte[] r = new byte[] { 0, (byte) 255 };
259 if (r[0] != 0 || mm[0] < 0.0)
262 r[1] = (byte) ((int) 128.0
263 + 127.0 * (sequenceFeature.score / mm[1]));
267 r[1] = (byte) ((int) 255.0 * (sequenceFeature.score / mm[1]));
273 boolean newFeatureAdded = false;
275 boolean findingFeatures = false;
277 protected boolean updateFeatures()
279 if (av.getFeaturesDisplayed() == null || renderOrder == null
283 if (av.getFeaturesDisplayed().getVisibleFeatureCount() < 1)
288 // TODO: decide if we should check for the visible feature count first
293 * search the alignment for all new features, give them a colour and display
294 * them. Then fires a PropertyChangeEvent on the changeSupport object.
297 protected void findAllFeatures()
299 synchronized (firing)
301 if (firing.equals(Boolean.FALSE))
303 firing = Boolean.TRUE;
304 findAllFeatures(true); // add all new features as visible
305 notifyFeaturesChanged();
306 firing = Boolean.FALSE;
312 public void notifyFeaturesChanged()
314 changeSupport.firePropertyChange("changeSupport", null, null);
318 public List<SequenceFeature> findFeaturesAtColumn(SequenceI sequence,
322 * include features at the position provided their feature type is
323 * displayed, and feature group is null or marked for display
325 List<SequenceFeature> result = new ArrayList<>();
326 if (!av.areFeaturesDisplayed() || getFeaturesDisplayed() == null)
331 Set<String> visibleFeatures = getFeaturesDisplayed()
332 .getVisibleFeatures();
333 String[] visibleTypes = visibleFeatures
334 .toArray(new String[visibleFeatures.size()]);
335 List<SequenceFeature> features = sequence.findFeatures(column, column,
339 * include features unless they are hidden (have no colour), based on
340 * feature group visibility, or a filter or colour threshold
342 for (SequenceFeature sf : features)
344 if (getColour(sf) != null)
353 * Searches alignment for all features and updates colours
355 * @param newMadeVisible
356 * if true newly added feature types will be rendered immediately
357 * TODO: check to see if this method should actually be proxied so
358 * repaint events can be propagated by the renderer code
361 public synchronized void findAllFeatures(boolean newMadeVisible)
363 newFeatureAdded = false;
367 newFeatureAdded = true;
371 findingFeatures = true;
372 if (av.getFeaturesDisplayed() == null)
374 av.setFeaturesDisplayed(new FeaturesDisplayed());
376 FeaturesDisplayedI featuresDisplayed = av.getFeaturesDisplayed();
378 Set<String> oldfeatures = new HashSet<>();
379 if (renderOrder != null)
381 for (int i = 0; i < renderOrder.length; i++)
383 if (renderOrder[i] != null)
385 oldfeatures.add(renderOrder[i]);
390 AlignmentI alignment = av.getAlignment();
391 List<String> allfeatures = new ArrayList<>();
393 for (int i = 0; i < alignment.getHeight(); i++)
395 SequenceI asq = alignment.getSequenceAt(i);
396 for (String group : asq.getFeatures().getFeatureGroups(true))
398 boolean groupDisplayed = true;
401 if (featureGroups.containsKey(group))
403 groupDisplayed = featureGroups.get(group);
407 groupDisplayed = newMadeVisible;
408 featureGroups.put(group, groupDisplayed);
413 Set<String> types = asq.getFeatures()
414 .getFeatureTypesForGroups(true, group);
415 for (String type : types)
417 if (!allfeatures.contains(type)) // or use HashSet and no test?
419 allfeatures.add(type);
421 updateMinMax(asq, type, true); // todo: for all features?
427 // uncomment to add new features in alphebetical order (but JAL-2575)
428 // Collections.sort(allfeatures, String.CASE_INSENSITIVE_ORDER);
431 for (String type : allfeatures)
433 if (!oldfeatures.contains(type))
435 featuresDisplayed.setVisible(type);
441 updateRenderOrder(allfeatures);
442 findingFeatures = false;
446 * Updates the global (alignment) min and max values for a feature type from
447 * the score for a sequence, if the score is not NaN. Values are stored
448 * separately for positional and non-positional features.
454 protected void updateMinMax(SequenceI seq, String featureType,
457 float min = seq.getFeatures().getMinimumScore(featureType, positional);
458 if (Float.isNaN(min))
463 float max = seq.getFeatures().getMaximumScore(featureType, positional);
467 * { {positionalMin, positionalMax}, {nonPositionalMin, nonPositionalMax} }
471 minmax = new Hashtable<>();
473 synchronized (minmax)
475 float[][] mm = minmax.get(featureType);
476 int index = positional ? 0 : 1;
479 mm = new float[][] { null, null };
480 minmax.put(featureType, mm);
482 if (mm[index] == null)
484 mm[index] = new float[] { min, max };
488 mm[index][0] = Math.min(mm[index][0], min);
489 mm[index][1] = Math.max(mm[index][1], max);
494 protected Boolean firing = Boolean.FALSE;
497 * replaces the current renderOrder with the unordered features in
498 * allfeatures. The ordering of any types in both renderOrder and allfeatures
499 * is preserved, and all new feature types are rendered on top of the existing
500 * types, in the order given by getOrder or the order given in allFeatures.
501 * Note. this operates directly on the featureOrder hash for efficiency. TODO:
502 * eliminate the float storage for computing/recalling the persistent ordering
503 * New Cability: updates min/max for colourscheme range if its dynamic
507 private void updateRenderOrder(List<String> allFeatures)
509 List<String> allfeatures = new ArrayList<>(allFeatures);
510 String[] oldRender = renderOrder;
511 renderOrder = new String[allfeatures.size()];
512 boolean initOrders = (featureOrder == null);
514 if (oldRender != null && oldRender.length > 0)
516 for (int j = 0; j < oldRender.length; j++)
518 if (oldRender[j] != null)
522 setOrder(oldRender[j],
523 (1 - (1 + (float) j) / oldRender.length));
525 if (allfeatures.contains(oldRender[j]))
527 renderOrder[opos++] = oldRender[j]; // existing features always
528 // appear below new features
529 allfeatures.remove(oldRender[j]);
532 float[][] mmrange = minmax.get(oldRender[j]);
535 FeatureColourI fc = featureColours.get(oldRender[j]);
536 if (fc != null && !fc.isSimpleColour() && fc.isAutoScaled()
537 && !fc.isColourByAttribute())
539 fc.updateBounds(mmrange[0][0], mmrange[0][1]);
547 if (allfeatures.size() == 0)
549 // no new features - leave order unchanged.
552 int i = allfeatures.size() - 1;
554 boolean sort = false;
555 String[] newf = new String[allfeatures.size()];
556 float[] sortOrder = new float[allfeatures.size()];
557 for (String newfeat : allfeatures)
562 // update from new features minmax if necessary
563 float[][] mmrange = minmax.get(newf[i]);
566 FeatureColourI fc = featureColours.get(newf[i]);
567 if (fc != null && !fc.isSimpleColour() && fc.isAutoScaled()
568 && !fc.isColourByAttribute())
570 fc.updateBounds(mmrange[0][0], mmrange[0][1]);
574 if (initOrders || !featureOrder.containsKey(newf[i]))
576 int denom = initOrders ? allfeatures.size() : featureOrder.size();
577 // new unordered feature - compute persistent ordering at head of
578 // existing features.
579 setOrder(newf[i], i / (float) denom);
581 // set order from newly found feature from persisted ordering.
582 sortOrder[i] = 2 - featureOrder.get(newf[i]).floatValue();
585 // only sort if we need to
586 sort = sort || sortOrder[i] > sortOrder[i + 1];
590 if (iSize > 1 && sort)
592 jalview.util.QuickSort.sort(sortOrder, newf);
595 System.arraycopy(newf, 0, renderOrder, opos, newf.length);
599 * get a feature style object for the given type string. Creates a
600 * java.awt.Color for a featureType with no existing colourscheme.
606 public FeatureColourI getFeatureStyle(String featureType)
608 FeatureColourI fc = featureColours.get(featureType);
611 Color col = ColorUtils.createColourFromName(featureType);
612 fc = new FeatureColour(col);
613 featureColours.put(featureType, fc);
619 public Color getColour(SequenceFeature feature)
621 FeatureColourI fc = getFeatureStyle(feature.getType());
622 return getColor(feature, fc);
626 * Answers true if the feature type is currently selected to be displayed,
632 public boolean showFeatureOfType(String type)
634 return type == null ? false
635 : (av.getFeaturesDisplayed() == null ? true
636 : av.getFeaturesDisplayed().isVisible(type));
640 public void setColour(String featureType, FeatureColourI col)
642 featureColours.put(featureType, col);
646 public void setTransparency(float value)
648 transparency = value;
652 public float getTransparency()
658 * analogous to colour - store a normalized ordering for all feature types in
659 * this rendering context.
662 * Feature type string
664 * normalized priority - 0 means always appears on top, 1 means
667 public float setOrder(String type, float position)
669 if (featureOrder == null)
671 featureOrder = new Hashtable<>();
673 featureOrder.put(type, Float.valueOf(position));
678 * get the global priority (0 (top) to 1 (bottom))
681 * @return [0,1] or -1 for a type without a priority
683 public float getOrder(String type)
685 if (featureOrder != null)
687 if (featureOrder.containsKey(type))
689 return featureOrder.get(type).floatValue();
696 public Map<String, FeatureColourI> getFeatureColours()
698 return featureColours;
702 * Replace current ordering with new ordering
705 * an array of { Type, Colour, Filter, Boolean }
706 * @return true if any visible features have been reordered, else false
708 public boolean setFeaturePriority(FeatureSettingsBean[] data)
710 return setFeaturePriority(data, true);
714 * Sets the priority order for features, with the highest priority (displayed
715 * on top) at the start of the data array
718 * an array of { Type, Colour, Filter, Boolean }
720 * when true current featureDisplay list will be cleared
721 * @return true if any visible features have been reordered or recoloured,
722 * else false (i.e. no need to repaint)
724 public boolean setFeaturePriority(FeatureSettingsBean[] data,
728 * note visible feature ordering and colours before update
730 List<String> visibleFeatures = getDisplayedFeatureTypes();
731 Map<String, FeatureColourI> visibleColours = new HashMap<>(
732 getFeatureColours());
734 FeaturesDisplayedI av_featuresdisplayed = null;
737 if ((av_featuresdisplayed = av.getFeaturesDisplayed()) != null)
739 av.getFeaturesDisplayed().clear();
743 av.setFeaturesDisplayed(
744 av_featuresdisplayed = new FeaturesDisplayed());
749 av_featuresdisplayed = av.getFeaturesDisplayed();
755 // The feature table will display high priority
756 // features at the top, but these are the ones
757 // we need to render last, so invert the data
758 renderOrder = new String[data.length];
762 for (int i = 0; i < data.length; i++)
764 String type = data[i].featureType;
765 setColour(type, data[i].featureColour);
768 av_featuresdisplayed.setVisible(type);
771 renderOrder[data.length - i - 1] = type;
776 * get the new visible ordering and return true if it has changed
777 * order or any colour has changed
779 List<String> reorderedVisibleFeatures = getDisplayedFeatureTypes();
780 if (!visibleFeatures.equals(reorderedVisibleFeatures))
783 * the list of ordered visible features has changed
789 * return true if any feature colour has changed
791 for (String feature : visibleFeatures)
793 if (visibleColours.get(feature) != getFeatureStyle(feature))
803 * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.beans.PropertyChangeListener)
806 public void addPropertyChangeListener(PropertyChangeListener listener)
808 changeSupport.addPropertyChangeListener(listener);
813 * @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.beans.PropertyChangeListener)
816 public void removePropertyChangeListener(PropertyChangeListener listener)
818 changeSupport.removePropertyChangeListener(listener);
821 public Set<String> getAllFeatureColours()
823 return featureColours.keySet();
826 public void clearRenderOrder()
831 public boolean hasRenderOrder()
833 return renderOrder != null;
837 * Returns feature types in ordering of rendering, where last means on top
839 public List<String> getRenderOrder()
841 if (renderOrder == null)
843 return Arrays.asList(new String[] {});
845 return Arrays.asList(renderOrder);
848 public int getFeatureGroupsSize()
850 return featureGroups != null ? 0 : featureGroups.size();
854 public List<String> getFeatureGroups()
856 // conflict between applet and desktop - featureGroups returns the map in
857 // the desktop featureRenderer
858 return (featureGroups == null) ? Arrays.asList(new String[0])
859 : Arrays.asList(featureGroups.keySet().toArray(new String[0]));
862 public boolean checkGroupVisibility(String group,
863 boolean newGroupsVisible)
865 if (featureGroups == null)
867 // then an exception happens next..
869 if (featureGroups.containsKey(group))
871 return featureGroups.get(group).booleanValue();
873 if (newGroupsVisible)
875 featureGroups.put(group, Boolean.valueOf(true));
882 * get visible or invisible groups
885 * true to return visible groups, false to return hidden ones.
886 * @return list of groups
889 public List<String> getGroups(boolean visible)
891 if (featureGroups != null)
893 List<String> gp = new ArrayList<>();
895 for (String grp : featureGroups.keySet())
897 Boolean state = featureGroups.get(grp);
898 if (state.booleanValue() == visible)
909 public void setGroupVisibility(String group, boolean visible)
911 featureGroups.put(group, Boolean.valueOf(visible));
915 public void setGroupVisibility(List<String> toset, boolean visible)
917 if (toset != null && toset.size() > 0 && featureGroups != null)
919 boolean rdrw = false;
920 for (String gst : toset)
922 Boolean st = featureGroups.get(gst);
923 featureGroups.put(gst, Boolean.valueOf(visible));
926 rdrw = rdrw || (visible != st.booleanValue());
931 // set local flag indicating redraw needed ?
937 public Map<String, FeatureColourI> getDisplayedFeatureCols()
939 Map<String, FeatureColourI> fcols = new Hashtable<>();
940 if (getViewport().getFeaturesDisplayed() == null)
944 Set<String> features = getViewport().getFeaturesDisplayed()
945 .getVisibleFeatures();
946 for (String feature : features)
948 fcols.put(feature, getFeatureStyle(feature));
954 public FeaturesDisplayedI getFeaturesDisplayed()
956 return av.getFeaturesDisplayed();
960 * Returns a (possibly empty) list of visible feature types, in render order
964 public List<String> getDisplayedFeatureTypes()
966 List<String> typ = getRenderOrder();
967 List<String> displayed = new ArrayList<>();
968 FeaturesDisplayedI feature_disp = av.getFeaturesDisplayed();
969 if (feature_disp != null)
971 synchronized (feature_disp)
973 for (String type : typ)
975 if (feature_disp.isVisible(type))
986 public List<String> getDisplayedFeatureGroups()
988 List<String> _gps = new ArrayList<>();
989 for (String gp : getFeatureGroups())
991 if (checkGroupVisibility(gp, false))
1000 * Answers true if the feature belongs to a feature group which is not
1001 * currently displayed, else false
1003 * @param sequenceFeature
1006 public boolean featureGroupNotShown(final SequenceFeature sequenceFeature)
1008 return featureGroups != null && sequenceFeature.featureGroup != null
1009 && sequenceFeature.featureGroup.length() != 0
1010 && featureGroups.containsKey(sequenceFeature.featureGroup)
1011 && !featureGroups.get(sequenceFeature.featureGroup)
1019 public List<SequenceFeature> findFeaturesAtResidue(SequenceI sequence,
1020 int fromResNo, int toResNo)
1022 List<SequenceFeature> result = new ArrayList<>();
1023 if (!av.areFeaturesDisplayed() || getFeaturesDisplayed() == null)
1029 * include features at the position provided their feature type is
1030 * displayed, and feature group is null or the empty string
1031 * or marked for display
1033 List<String> visibleFeatures = getDisplayedFeatureTypes();
1034 String[] visibleTypes = visibleFeatures
1035 .toArray(new String[visibleFeatures.size()]);
1036 List<SequenceFeature> features = sequence.getFeatures()
1037 .findFeatures(fromResNo, toResNo, visibleTypes);
1039 for (SequenceFeature sf : features)
1041 if (!featureGroupNotShown(sf) && getColour(sf) != null)
1050 * Removes from the list of features any whose group is not shown, or that are
1051 * visible and duplicate the location of a visible feature of the same type.
1052 * Should be used only for features of the same, simple, feature colour (which
1053 * normally implies the same feature type). No filtering is done if
1054 * transparency, or any feature filters, are in force.
1058 public void filterFeaturesForDisplay(List<SequenceFeature> features)
1061 * fudge: JalviewJS's IntervalStore lacks the sort method called :-(
1063 if (Platform.isJS())
1069 * don't remove 'redundant' features if
1070 * - transparency is applied (feature count affects depth of feature colour)
1071 * - filters are applied (not all features may be displayable)
1073 if (features.isEmpty() || transparency != 1f
1074 || !featureFilters.isEmpty())
1079 SequenceFeatures.sortFeatures(features, true);
1080 SequenceFeature lastFeature = null;
1082 Iterator<SequenceFeature> it = features.iterator();
1083 while (it.hasNext())
1085 SequenceFeature sf = it.next();
1086 if (featureGroupNotShown(sf))
1093 * a feature is redundant for rendering purposes if it has the
1094 * same extent as another (so would just redraw the same colour);
1095 * (checking type and isContactFeature as a fail-safe here, although
1096 * currently they are guaranteed to match in this context)
1098 if (lastFeature != null && sf.getBegin() == lastFeature.getBegin()
1099 && sf.getEnd() == lastFeature.getEnd()
1100 && sf.isContactFeature() == lastFeature.isContactFeature()
1101 && sf.getType().equals(lastFeature.getType()))
1110 public Map<String, FeatureMatcherSetI> getFeatureFilters()
1112 return featureFilters;
1116 public void setFeatureFilters(Map<String, FeatureMatcherSetI> filters)
1118 featureFilters = filters;
1122 public FeatureMatcherSetI getFeatureFilter(String featureType)
1124 return featureFilters.get(featureType);
1128 public void setFeatureFilter(String featureType,
1129 FeatureMatcherSetI filter)
1131 if (filter == null || filter.isEmpty())
1133 featureFilters.remove(featureType);
1137 featureFilters.put(featureType, filter);
1142 * Answers the colour for the feature, or null if the feature is excluded by
1143 * feature group visibility, by filters, or by colour threshold settings. This
1144 * method does not take feature type visibility into account.
1150 public Color getColor(SequenceFeature sf, FeatureColourI fc)
1153 * is the feature group displayed?
1155 if (featureGroupNotShown(sf))
1161 * does the feature pass filters?
1163 if (!featureMatchesFilters(sf))
1168 return fc.getColor(sf);
1172 * Answers true if there no are filters defined for the feature type, or this
1173 * feature matches the filters. Answers false if the feature fails to match
1179 protected boolean featureMatchesFilters(SequenceFeature sf)
1181 FeatureMatcherSetI filter = featureFilters.get(sf.getType());
1182 return filter == null ? true : filter.matches(sf);
1186 * Answers true unless the specified group is set to hidden. Defaults to true
1187 * if group visibility is not set.
1192 public boolean isGroupVisible(String group)
1194 if (!featureGroups.containsKey(group))
1198 return featureGroups.get(group);
1202 * Orders features in render precedence (last in order is last to render, so
1203 * displayed on top of other features)
1207 public void orderFeatures(Comparator<String> order)
1209 Arrays.sort(renderOrder, order);
1213 public MappedFeatures findComplementFeaturesAtResidue(
1214 final SequenceI sequence, final int pos)
1216 SequenceI ds = sequence.getDatasetSequence();
1221 final char residue = ds.getCharAt(pos - ds.getStart());
1223 List<SequenceFeature> found = new ArrayList<>();
1224 List<AlignedCodonFrame> mappings = this.av.getAlignment()
1225 .getCodonFrame(sequence);
1228 * fudge: if no mapping found, check the complementary alignment
1229 * todo: only store in one place? StructureSelectionManager?
1231 if (mappings.isEmpty())
1233 mappings = this.av.getCodingComplement().getAlignment()
1234 .getCodonFrame(sequence);
1238 * todo: direct lookup of CDS for peptide and vice-versa; for now,
1239 * have to search through an unordered list of mappings for a candidate
1241 SequenceToSequenceMapping mapping = null;
1242 SequenceI mapFrom = null;
1244 for (AlignedCodonFrame acf : mappings)
1246 mapping = acf.getCoveringCodonMapping(ds);
1247 if (mapping == null)
1251 SearchResultsI sr = new SearchResults();
1252 mapping.markMappedRegion(ds, pos, sr);
1253 for (SearchResultMatchI match : sr.getResults())
1255 int fromRes = match.getStart();
1256 int toRes = match.getEnd();
1257 mapFrom = match.getSequence();
1258 List<SequenceFeature> fs = findFeaturesAtResidue(mapFrom, fromRes,
1260 for (SequenceFeature sf : fs)
1262 if (!found.contains(sf))
1270 * just take the first mapped features we find
1272 if (!found.isEmpty())
1277 if (found.isEmpty())
1283 * sort by renderorder (inefficiently but ok for small scale);
1284 * NB this sorts 'on top' feature to end, for rendering
1286 List<SequenceFeature> result = new ArrayList<>();
1287 final int toAdd = found.size();
1289 for (String type : renderOrder)
1291 for (SequenceFeature sf : found)
1293 if (type.equals(sf.getType()))
1305 return new MappedFeatures(mapping.getMapping(), mapFrom, pos, residue,
1310 public boolean isVisible(SequenceFeature feature)
1312 if (feature == null)
1316 if (getFeaturesDisplayed() == null
1317 || !getFeaturesDisplayed().isVisible(feature.getType()))
1321 if (featureGroupNotShown(feature))
1325 FeatureColourI fc = featureColours.get(feature.getType());
1326 if (fc != null && fc.isOutwithThreshold(feature))
1330 if (!featureMatchesFilters(feature))
1338 public AlignmentViewPanel getAlignPanel()