X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fschemes%2FFeatureColour.java;h=54d1c6cf76a45bff99cc020c18dc90e12ffe6c18;hb=0ca13d2d06bf5ee99eb7dc123ee7ffcea016f733;hp=ce382c3af84267a67a7884241a3020e8beecd530;hpb=0b1c761dfaa8242f122cf868e8897a06ec6eb727;p=jalview.git diff --git a/src/jalview/schemes/FeatureColour.java b/src/jalview/schemes/FeatureColour.java index ce382c3..54d1c6c 100644 --- a/src/jalview/schemes/FeatureColour.java +++ b/src/jalview/schemes/FeatureColour.java @@ -1,15 +1,40 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ package jalview.schemes; import jalview.api.FeatureColourI; import jalview.datamodel.SequenceFeature; +import jalview.util.ColorUtils; +import jalview.util.Format; import java.awt.Color; +import java.util.StringTokenizer; /** * A class that wraps either a simple colour or a graduated colour */ public class FeatureColour implements FeatureColourI { + private static final String BAR = "|"; + final private Color colour; final private Color minColour; @@ -49,6 +74,204 @@ public class FeatureColour implements FeatureColourI final private float deltaBlue; /** + * Parses a Jalview features file format colour descriptor + * [label|][mincolour|maxcolour + * |[absolute|]minvalue|maxvalue|thresholdtype|thresholdvalue] Examples: + * + * + * @param descriptor + * @return + * @throws IllegalArgumentException + * if not parseable + */ + public static FeatureColour parseJalviewFeatureColour(String descriptor) + { + StringTokenizer gcol = new StringTokenizer(descriptor, "|", true); + float min = Float.MIN_VALUE; + float max = Float.MAX_VALUE; + boolean labelColour = false; + + String mincol = gcol.nextToken(); + if (mincol == "|") + { + throw new IllegalArgumentException( + "Expected either 'label' or a colour specification in the line: " + + descriptor); + } + String maxcol = null; + if (mincol.toLowerCase().indexOf("label") == 0) + { + labelColour = true; + mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null); + // skip '|' + mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null); + } + + if (!labelColour && !gcol.hasMoreTokens()) + { + /* + * only a simple colour specification - parse it + */ + Color colour = ColorUtils.parseColourString(descriptor); + if (colour == null) + { + throw new IllegalArgumentException( + "Invalid colour descriptor: " + descriptor); + } + return new FeatureColour(colour); + } + + /* + * autoScaled == true: colours range over actual score range + * autoScaled == false ('abso'): colours range over min/max range + */ + boolean autoScaled = true; + String tok = null, minval, maxval; + if (mincol != null) + { + // at least four more tokens + if (mincol.equals("|")) + { + mincol = ""; + } + else + { + gcol.nextToken(); // skip next '|' + } + maxcol = gcol.nextToken(); + if (maxcol.equals("|")) + { + maxcol = ""; + } + else + { + gcol.nextToken(); // skip next '|' + } + tok = gcol.nextToken(); + gcol.nextToken(); // skip next '|' + if (tok.toLowerCase().startsWith("abso")) + { + minval = gcol.nextToken(); + gcol.nextToken(); // skip next '|' + autoScaled = false; + } + else + { + minval = tok; + } + maxval = gcol.nextToken(); + if (gcol.hasMoreTokens()) + { + gcol.nextToken(); // skip next '|' + } + try + { + if (minval.length() > 0) + { + min = new Float(minval).floatValue(); + } + } catch (Exception e) + { + throw new IllegalArgumentException( + "Couldn't parse the minimum value for graduated colour (" + + descriptor + ")"); + } + try + { + if (maxval.length() > 0) + { + max = new Float(maxval).floatValue(); + } + } catch (Exception e) + { + throw new IllegalArgumentException( + "Couldn't parse the maximum value for graduated colour (" + + descriptor + ")"); + } + } + else + { + // add in some dummy min/max colours for the label-only + // colourscheme. + mincol = "FFFFFF"; + maxcol = "000000"; + } + + /* + * construct the FeatureColour + */ + FeatureColour featureColour; + try + { + Color minColour = ColorUtils.parseColourString(mincol); + Color maxColour = ColorUtils.parseColourString(maxcol); + featureColour = new FeatureColour(minColour, maxColour, min, max); + featureColour.setColourByLabel(labelColour); + featureColour.setAutoScaled(autoScaled); + // add in any additional parameters + String ttype = null, tval = null; + if (gcol.hasMoreTokens()) + { + // threshold type and possibly a threshold value + ttype = gcol.nextToken(); + if (ttype.toLowerCase().startsWith("below")) + { + featureColour.setBelowThreshold(true); + } + else if (ttype.toLowerCase().startsWith("above")) + { + featureColour.setAboveThreshold(true); + } + else + { + if (!ttype.toLowerCase().startsWith("no")) + { + System.err.println( + "Ignoring unrecognised threshold type : " + ttype); + } + } + } + if (featureColour.hasThreshold()) + { + try + { + gcol.nextToken(); + tval = gcol.nextToken(); + featureColour.setThreshold(new Float(tval).floatValue()); + } catch (Exception e) + { + System.err.println("Couldn't parse threshold value as a float: (" + + tval + ")"); + } + } + if (gcol.hasMoreTokens()) + { + System.err.println( + "Ignoring additional tokens in parameters in graduated colour specification\n"); + while (gcol.hasMoreTokens()) + { + System.err.println("|" + gcol.nextToken()); + } + System.err.println("\n"); + } + return featureColour; + } catch (Exception e) + { + throw new IllegalArgumentException(e.getMessage()); + } + } + + /** * Default constructor */ public FeatureColour() @@ -63,8 +286,8 @@ public class FeatureColour implements FeatureColourI */ public FeatureColour(Color c) { - minColour = null; - maxColour = null; + minColour = Color.WHITE; + maxColour = Color.BLACK; minRed = 0f; minGreen = 0f; minBlue = 0f; @@ -84,6 +307,14 @@ public class FeatureColour implements FeatureColourI */ public FeatureColour(Color low, Color high, float min, float max) { + if (low == null) + { + low = Color.white; + } + if (high == null) + { + high = Color.black; + } graduatedColour = true; colour = null; minColour = low; @@ -115,6 +346,7 @@ public class FeatureColour implements FeatureColourI */ public FeatureColour(FeatureColour fc) { + graduatedColour = fc.graduatedColour; colour = fc.colour; minColour = fc.minColour; maxColour = fc.maxColour; @@ -133,9 +365,10 @@ public class FeatureColour implements FeatureColourI setAutoScaled(fc.isAutoScaled()); setColourByLabel(fc.isColourByLabel()); } - + /** * Copy constructor with new min/max ranges + * * @param fc * @param min * @param max @@ -157,8 +390,7 @@ public class FeatureColour implements FeatureColourI * Sets the 'graduated colour' flag. If true, also sets 'colour by label' to * false. */ - @Override - public void setGraduatedColour(boolean b) + void setGraduatedColour(boolean b) { graduatedColour = b; if (b) @@ -204,6 +436,7 @@ public class FeatureColour implements FeatureColourI setGraduatedColour(false); } } + @Override public boolean isBelowThreshold() { @@ -309,8 +542,7 @@ public class FeatureColour implements FeatureColourI { if (isColourByLabel()) { - return UserColourScheme - .createColourFromName(feature.getDescription()); + return ColorUtils.createColourFromName(feature.getDescription()); } if (!isGraduatedColour()) @@ -318,16 +550,27 @@ public class FeatureColour implements FeatureColourI return getColour(); } - // todo should we check for above/below threshold here? - if (range == 0.0) - { - return getMaxColour(); - } + /* + * graduated colour case, optionally with threshold + * Float.NaN is assigned minimum visible score colour + */ float scr = feature.getScore(); if (Float.isNaN(scr)) { return getMinColour(); } + if (isAboveThreshold() && scr <= threshold) + { + return null; + } + if (isBelowThreshold() && scr >= threshold) + { + return null; + } + if (range == 0.0) + { + return getMaxColour(); + } float scl = (scr - base) / range; if (isHighToLow) { @@ -341,7 +584,8 @@ public class FeatureColour implements FeatureColourI { scl = 1f; } - return new Color(minRed + scl * deltaRed, minGreen + scl * deltaGreen, minBlue + scl * deltaBlue); + return new Color(minRed + scl * deltaRed, minGreen + scl * deltaGreen, + minBlue + scl * deltaBlue); } /** @@ -368,44 +612,6 @@ public class FeatureColour implements FeatureColourI return (isHighToLow) ? (base + range) : base; } - /** - * Answers true if the feature has a simple colour, or is coloured by label, - * or has a graduated colour and the score of this feature instance is within - * the range to render (if any), i.e. does not lie below or above any - * threshold set. - * - * @param feature - * @return - */ - @Override - public boolean isColored(SequenceFeature feature) - { - if (isColourByLabel() || !isGraduatedColour()) - { - return true; - } - - float val = feature.getScore(); - if (Float.isNaN(val)) - { - return true; - } - if (Float.isNaN(this.threshold)) - { - return true; - } - - if (isAboveThreshold() && val <= threshold) - { - return false; - } - if (isBelowThreshold() && val >= threshold) - { - return false; - } - return true; - } - @Override public boolean isSimpleColour() { @@ -418,4 +624,54 @@ public class FeatureColour implements FeatureColourI return isAboveThreshold() || isBelowThreshold(); } + @Override + public String toJalviewFormat(String featureType) + { + String colourString = null; + if (isSimpleColour()) + { + colourString = Format.getHexString(getColour()); + } + else + { + StringBuilder sb = new StringBuilder(32); + if (isColourByLabel()) + { + sb.append("label"); + if (hasThreshold()) + { + sb.append(BAR).append(BAR).append(BAR); + } + } + if (isGraduatedColour()) + { + sb.append(Format.getHexString(getMinColour())).append(BAR); + sb.append(Format.getHexString(getMaxColour())).append(BAR); + if (!isAutoScaled()) + { + sb.append("abso").append(BAR); + } + } + if (hasThreshold() || isGraduatedColour()) + { + sb.append(getMin()).append(BAR); + sb.append(getMax()).append(BAR); + if (isBelowThreshold()) + { + sb.append("below").append(BAR).append(getThreshold()); + } + else if (isAboveThreshold()) + { + sb.append("above").append(BAR).append(getThreshold()); + } + else + { + sb.append("none"); + } + } + colourString = sb.toString(); + } + return String.format("%s\t%s", featureType, colourString); + } + }