import java.awt.Color; import java.util.Collections; import java.util.Vector; interface ColourScheme { public void setScaleBasedOnPixels(DataNormalizer below,DataNormalizer above); public void setSensitivity(float sensitivity_slider_position); public void doLoopPrecomputation(); public Color getPixelColour(float below,float above); public String getKeyXAxisLabel(); public String getKeyYAxisLabel(); public float getKeyXAxisTickmark(int x); public float getKeyYAxisTickmark(int y); public Color getKeyColour(int x,int y); public int getKeyXSize(); public int getKeyYSize(); public class BrewerSequentialSequential implements ColourScheme { final static Color[][] brewer_sequential_sequential = { { new Color(232,232,232), new Color(181,211,231), new Color(79,173,208) }, { new Color(229,180,217), new Color(179,179,179), new Color(57,131,187) }, { new Color(222,79,166), new Color(176,53,152), new Color(42,42,42) } }; private double mean,std,sensitivity; private float lower_thresh,upper_thresh; private double transform (double x) { return Math.log(x); } private double untransform (double x) { return Math.exp(x); } @Override public void setScaleBasedOnPixels(DataNormalizer below, DataNormalizer above) { //calculate mean and std of all nonzero flows from both arrays assert(below.length==above.length); double n = 0; double total = 0; for (int i=0;i0) { n++; total += transform(belowdata); } if (abovedata>0) { n++; total += transform(abovedata); } } mean = total / n; double variance = 0; for (int i=0;i0) { variance += Math.pow(transform(belowdata) - mean,2); } if (abovedata>0) { variance += Math.pow(transform(abovedata) - mean,2); } } std = Math.pow(variance/n,0.5); } @Override public void setSensitivity(float sensitivity_slider_position) { sensitivity = Math.exp((sensitivity_slider_position-0.5)*5); } @Override public void doLoopPrecomputation() { lower_thresh = (float) untransform(mean-std*sensitivity); upper_thresh = (float) untransform(mean+std*sensitivity); } @Override public Color getPixelColour(float below, float above) { int colorspacex, colorspacey; if (above < lower_thresh) colorspacey = 0; else if (above > upper_thresh) colorspacey = 2; else colorspacey = 1; if (below < lower_thresh) colorspacex = 0; else if (below > upper_thresh) colorspacex = 2; else colorspacex = 1; return brewer_sequential_sequential[colorspacex][colorspacey]; } @Override public String getKeyXAxisLabel() { return "LOWER BAND"; } @Override public String getKeyYAxisLabel() { return "UPPER BAND"; } @Override public float getKeyXAxisTickmark(int x) { if (x==0) return lower_thresh; else return upper_thresh; } @Override public float getKeyYAxisTickmark(int y) { return getKeyXAxisTickmark(y); // the same thing, in this class } @Override public Color getKeyColour(int x, int y) { return brewer_sequential_sequential[x][y]; } @Override public int getKeyXSize() { return 3; } @Override public int getKeyYSize() { return 3; } } public class BrewerDivergingSequential implements ColourScheme { protected double quantitymean; protected double quantitystd; protected double sensitivity; private float quantity_lower_thresh, quantity_upper_thresh, balance_lower_thresh, balance_upper_thresh; final static Color[][] brewer_diverging_sequential = { { new Color(255,204,128), new Color(230,230,230), new Color(195,179,216) }, { new Color(243,89,38), new Color(191,191,191), new Color(123,103,171) }, { new Color(179,0,0), new Color(127,127,127), new Color(36,13,94) } }; private double getBalance(float below,float above) { float total = below+above; if (total!=0) return -(below-above)/total; else return 0; } private double transform(double x) { return Math.log(x); } private double untransform(double x) { return Math.exp(x); } @Override public void setScaleBasedOnPixels(DataNormalizer below, DataNormalizer above) { assert(below.length==above.length); //calculate mean and std of all nonzero flows double n = 0; double quantitytotal = 0; for (int i=0;i0) { n++; quantitytotal += transform(quantity); } } quantitymean = quantitytotal / n; double quantityvariance = 0; for (int i=0;i0) quantityvariance += Math.pow(transform(quantity) - quantitymean,2); } quantitystd = Math.pow(quantityvariance/n,0.5); } @Override public void setSensitivity(float sensitivity_slider_position) { sensitivity = Math.exp((sensitivity_slider_position-0.5)*5); } @Override public void doLoopPrecomputation() { quantity_lower_thresh = (float) untransform(quantitymean-quantitystd*sensitivity); quantity_upper_thresh = (float) untransform(quantitymean+quantitystd*sensitivity); balance_lower_thresh = (float) -0.2; balance_upper_thresh = (float) 0.2; } @Override public Color getPixelColour(float below, float above) { double quantity = below+above; double balance = getBalance(below,above); int colorspacex, colorspacey; if (quantity < quantity_lower_thresh) colorspacex = 0; else if (quantity > quantity_upper_thresh) colorspacex = 2; else colorspacex = 1; if (balance < balance_lower_thresh) colorspacey = 0; else if (balance > balance_upper_thresh) colorspacey = 2; else colorspacey = 1; return brewer_diverging_sequential[colorspacex][colorspacey]; } @Override public String getKeyXAxisLabel() { return "COMPOSITION: (U-L) / (U+L)"; } @Override public String getKeyYAxisLabel() { return "TOTAL FLOW"; } @Override public float getKeyXAxisTickmark(int x) { if (x==0) return balance_lower_thresh; else return balance_upper_thresh; } @Override public float getKeyYAxisTickmark(int y) { if (y==0) return quantity_lower_thresh; else return quantity_upper_thresh; } @Override public Color getKeyColour(int x, int y) { return brewer_diverging_sequential[y][x]; } @Override public int getKeyXSize() { return 3; } @Override public int getKeyYSize() { return 3; } } public class UnivariateDiverging implements ColourScheme { final static Color[] univariate_diverging = new Color[] {new Color(178,24,43),new Color(239,138,98),new Color(253,219,199), new Color(247,247,247), new Color(209,229,240), new Color(103,169,207), new Color(33,102,172)}; Vector quantity_thresholds; private double sensitivity; private double quantitymean; private double quantitystd; @Override public void doLoopPrecomputation() { quantity_thresholds = new Vector(); quantity_thresholds.add((float) (quantitymean-quantitystd*sensitivity*3)); quantity_thresholds.add((float) (quantitymean-quantitystd*sensitivity*2)); quantity_thresholds.add((float) (quantitymean-quantitystd*sensitivity)); quantity_thresholds.add((float) (quantitymean+quantitystd*sensitivity)); quantity_thresholds.add((float) (quantitymean+quantitystd*sensitivity*2)); quantity_thresholds.add((float) (quantitymean+quantitystd*sensitivity*3)); } @Override public Color getPixelColour(float below, float above) { float total = below+above; //binarySearch returns the index if found, or (-(next element)-1) if not //so +1 then take absolute value to get colour class :- //it's all a bit clearer if you draw yourself an array slicing diagram! int index = Math.abs(Collections.binarySearch(quantity_thresholds, total)+1); return univariate_diverging[index]; } @Override public String getKeyXAxisLabel() { return ""; } @Override public String getKeyYAxisLabel() { return "TOTAL FLOW"; } @Override public float getKeyXAxisTickmark(int x) { return 0; } @Override public float getKeyYAxisTickmark(int y) { return quantity_thresholds.get(y); } @Override public Color getKeyColour(int x, int y) { return univariate_diverging[y]; } @Override public int getKeyXSize() { return 1; } @Override public int getKeyYSize() { return 7; } @Override public void setScaleBasedOnPixels(DataNormalizer below, DataNormalizer above) { assert(below.length==above.length); double n = Math.pow(above.length,2); double quantitytotal = 0; for (int i=0;i