Skip to content

Commit

Permalink
Merge pull request #122 from Duke-GCB/reverse_pref_colors
Browse files Browse the repository at this point in the history
Reverse preference colors and only show predictions fully in range
  • Loading branch information
johnbradley authored Nov 10, 2016
2 parents 7023410 + 2d51d8f commit 7027f6c
Show file tree
Hide file tree
Showing 13 changed files with 241 additions and 714 deletions.
460 changes: 84 additions & 376 deletions imadsconf.yaml

Large diffs are not rendered by default.

112 changes: 32 additions & 80 deletions portal/src/app/models/ColorBlender.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,12 @@ COLOR_RGB[GREEN_COLOR_NAME] = [0, 128, 0];
COLOR_RGB[BLUE_COLOR_NAME] = [0, 0, 255];

// PREFERENCE_COLORS based on https://github.com/Duke-GCB/TrackHubGenerator/blob/master/cwl/bin/add_itemrgb_column.py
export let PREFERENCE_GRAY = [190, 190, 190];
// The preferred length of a preference array.
export let PREFERRED_PREFERENCE_ARRAY_LENGTH = 9;
let PREFERENCE_GRAY = [190, 190, 190];
/**
* Color constants for Red gradient
* From Colorbrewer Red 9 http://colorbrewer2.org/?type=sequential&scheme=Reds&n=9
*/
export let PREFERENCE_REDS = [
let PREFERENCE_REDS = [
[255, 245, 240],
[254, 224, 210],
[252, 187, 161],
Expand All @@ -35,14 +33,15 @@ export let PREFERENCE_REDS = [
];

/*
* Color constants for blue gradient
* Converted to RGB from R Color names
* plotclr2 = c("midnightblue","steelblue4","steelblue","steelblue3","steelblue2","steelblue1")
* using chart at http://research.stowers-institute.org/efg/R/Color/Chart/ColorChart.pdf
* midnightblue is the darkest, should be associated with the highest value,
* and therefore be last.
*/
export let PREFERENCE_BLUES = [
# Color constants for blue gradient
# Converted to RGB from R Color names
# plotclr2 = c("midnightblue","steelblue4","steelblue","steelblue3","steelblue2","steelblue1")
# using chart at http://research.stowers-institute.org/efg/R/Color/Chart/ColorChart.pdf
# midnightblue is the darkest, should be associated with the highest value,
# and therefore be last.
*/
let PREFERENCE_BLUES = [
[99, 184, 255], // steelblue1
[92, 172, 238], // steelblue2
[79, 148, 205], // steelblue3
Expand All @@ -55,7 +54,7 @@ export let PREFERENCE_BLUES = [
* Color constants for green gradient
* http://colorbrewer2.org/?type=sequential&scheme=Reds&n=9#type=sequential&scheme=Greens&n=9
*/
export let PREFERENCE_GREENS = [
let PREFERENCE_GREENS = [
[247,252,245],
[229,245,224],
[199,233,192],
Expand Down Expand Up @@ -91,99 +90,52 @@ export default class ColorBlender {
}
}

/**
* Using this.value and the preferenceBin array determine which color slot we should show.
* @returns {number} integer 0 -> bins.length
*/
getPreferenceSlot() {
let bins = this.getPreferenceBin();
let slot = 0; // if greater than all bins must be the last slot
let absValue = Math.abs(this.value);
for (let i = 0; i < bins.length; i++) {
let binValue = Math.abs(bins[i]);
if (binValue > absValue) {
break;
}
slot = i + 1;
}
return slot;
}

/**
* Return array of inner fenceposts used to determine which slot in color arrays we should use.
* If you have three colors ...-1, 1-10, 10-... it returns an array like so: [1, 10]
* Anything below the lowest number is assumed to be the first bin anything greater than the
* last fencepost is assumed to be the last bin.
* @returns array of inner fencepost cutoff values
*/
getPreferenceBin() {
getScaledValue() {
if (this.isNegative()) {
return this.predictionColor.preferenceBins.neg;
return ColorBlender.scaleValue(this.value, this.predictionColor.preferenceMin);
} else {
return this.predictionColor.preferenceBins.pos;
return ColorBlender.scaleValue(this.value, this.predictionColor.preferenceMax);
}
}

/**
* Return array of r,g,b values for this.value.
* @param colorName determines which color array to use eg: 'red'
* @returns array of r g b
*/
getPreferenceColorArray(colorName) {
let slot = this.getPreferenceSlot();
let colorArray = PREF_ARRAY_LOOKUP[colorName];
if (colorArray.length != PREFERRED_PREFERENCE_ARRAY_LENGTH) {
slot = this.scalePreferenceSlot(slot, colorArray.length);
static scaleValue(value, limitValue) {
if (limitValue) {
return value / Math.abs(limitValue);
}
return colorArray[slot];
return value;
}

/**
* Return array of r,g,b values for this.value.
* @param colorName determines which color array to use eg: 'red'
* @returns array of r g b
*/
scalePreferenceSlot(slot, arrayLength) {
let scaleFactor = parseFloat(arrayLength) / parseFloat(PREFERRED_PREFERENCE_ARRAY_LENGTH);
let scaledSlot = slot * scaleFactor;
return Math.round(scaledSlot);
}

/**
* Return CSS color string appropriate for this.value and color settings.
*/
getColor() {
let colorName = this.determineColorName();
if (this.isPreference) {
return this.getPreferenceColor(colorName);
}
let colorRGB = COLOR_RGB[colorName];
let zeroColor = [255, 255, 255];
let colorValues = ColorBlender.interpolateRGB(zeroColor, colorRGB, this.value);
let colorValues = ColorBlender.interpolateRGB(zeroColor, colorRGB, this.getScaledValue());
return this.getRGBString(colorValues);
}

/**
* Returns an array of color values based on colorPreference.color1/2 and this.value being postitive or neg.
* @returns array of colors(array)
*/
getPreferenceArray() {
let colorName = this.determineColorName();
return PREF_ARRAY_LOOKUP[colorName];
}

/**
* Returns CSS color "rgb(r,g,b)" for this.value.
* Looks up which array to use based on colorName.
* Determines which slot in the array to use based on preferenceBins.
* @param colorName name of the color array to use eg: 'red'
* @returns CSS color string
*/
getPreferenceColor(colorName) {
let value = this.value;
let value = this.getScaledValue();
let colorRGB = PREFERENCE_GRAY;
if (value > POS_FLOAT_CUTOOFF || value < NEG_FLOAT_CUTOOFF) {
colorRGB = this.getPreferenceColorArray(colorName);
let gradientArray = this.getPreferenceArray();
let slots = ColorBlender.determineSlots(value, gradientArray.length);
colorRGB = gradientArray[slots[0]];
if (slots[0] != slots[1]) {
let zeroColor = gradientArray[slots[0]];
let oneColor = gradientArray[slots[1]];
//need to scale value for the two slots
let slotScaledValue = ColorBlender.scaleValueForSlot(slots[0], gradientArray.length, value);
let blendedColor = ColorBlender.interpolateRGB(zeroColor, oneColor, slotScaledValue);
return this.getRGBString(blendedColor);
}
}
return this.getRGBString({
'r': colorRGB[0],
Expand Down
3 changes: 2 additions & 1 deletion portal/src/app/models/GenomeData.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export function getPreferenceSettings(genomeData, genomeName, modelName) {
if (modelObj.data_type == PREFERENCE_TYPE) {
return {
isPreference: true,
preferenceBins: modelObj.preference_bins,
preferenceMin: modelObj.preference_min,
preferenceMax: modelObj.preference_max,
}
}
}
Expand Down
142 changes: 17 additions & 125 deletions portal/src/tests/testColorBlender.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import ColorBlender, {RED_COLOR_NAME, GREEN_COLOR_NAME, BLUE_COLOR_NAME, YELLOW_COLOR_NAME,
CYAN_COLOR_NAME, MAGENTA_COLOR_NAME,
PREFERENCE_GRAY, PREFERENCE_REDS, PREFERENCE_BLUES, PREFERENCE_GREENS} from './../app/models/ColorBlender.js';
CYAN_COLOR_NAME, MAGENTA_COLOR_NAME} from './../app/models/ColorBlender.js';
var assert = require('chai').assert;

function rgbFromArray(items) {
return 'rgb(' + items[0] + ',' + items[1] + ',' + items[2] + ')';
}

describe('ColorBlender', function () {
describe('isNegative()', function () {
it('true for values < 0', function () {
Expand All @@ -31,103 +26,22 @@ describe('ColorBlender', function () {
});
});

describe('getPreferenceSlot()', function () {
it('works with two bins', function () {
let predictionColor = {
'preferenceBins': {
'pos': [2],
'neg': [4]
}
};
assert.equal(0, new ColorBlender(1.0, predictionColor).getPreferenceSlot());
assert.equal(0, new ColorBlender(1.99, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(2.0, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(3.0, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(200.0, predictionColor).getPreferenceSlot());
assert.equal(0, new ColorBlender(-1.0, predictionColor).getPreferenceSlot());
assert.equal(0, new ColorBlender(-3.99, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(-4, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(-4.1, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(-4000.0, predictionColor).getPreferenceSlot());
});
it('works with three bins', function () {
let predictionColor = {
'preferenceBins': {
'pos': [1,8],
'neg': [2,6]
}
};
assert.equal(0, new ColorBlender(0.1, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(1.0, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(1.1, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(7.9, predictionColor).getPreferenceSlot());
assert.equal(2, new ColorBlender(8.0, predictionColor).getPreferenceSlot());
assert.equal(2, new ColorBlender(8.1, predictionColor).getPreferenceSlot());
assert.equal(2, new ColorBlender(100, predictionColor).getPreferenceSlot());

assert.equal(0, new ColorBlender(-0.1, predictionColor).getPreferenceSlot());
assert.equal(0, new ColorBlender(-1.9, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(-2.0, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(-2.1, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(-5.9, predictionColor).getPreferenceSlot());
assert.equal(2, new ColorBlender(-6.0, predictionColor).getPreferenceSlot());
assert.equal(2, new ColorBlender(-6.1, predictionColor).getPreferenceSlot());
assert.equal(2, new ColorBlender(-100.0, predictionColor).getPreferenceSlot());
});
it('works with four bins', function () {
let predictionColor = {
'preferenceBins': {
'pos': [1, 8, 10],
'neg': [2, 6, 20]
}
};
assert.equal(0, new ColorBlender(0.1, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(1.0, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(1.1, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(7.9, predictionColor).getPreferenceSlot());
assert.equal(2, new ColorBlender(8.0, predictionColor).getPreferenceSlot());
assert.equal(2, new ColorBlender(8.1, predictionColor).getPreferenceSlot());
assert.equal(3, new ColorBlender(10, predictionColor).getPreferenceSlot());
assert.equal(3, new ColorBlender(11, predictionColor).getPreferenceSlot());

assert.equal(0, new ColorBlender(-0.1, predictionColor).getPreferenceSlot());
assert.equal(0, new ColorBlender(-1.9, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(-2.0, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(-2.1, predictionColor).getPreferenceSlot());
assert.equal(1, new ColorBlender(-5.9, predictionColor).getPreferenceSlot());
assert.equal(2, new ColorBlender(-6.0, predictionColor).getPreferenceSlot());
assert.equal(2, new ColorBlender(-6.1, predictionColor).getPreferenceSlot());
assert.equal(2, new ColorBlender(-19.9, predictionColor).getPreferenceSlot());
assert.equal(3, new ColorBlender(-20.0, predictionColor).getPreferenceSlot());
assert.equal(3, new ColorBlender(-30.0, predictionColor).getPreferenceSlot());
describe('getScaledValue()', function () {
it('just returns value when no preferenceMin or preferenceMax', function () {
let predictionColor = {};
assert.equal(10, new ColorBlender(10, predictionColor).getScaledValue());
assert.equal(-10, new ColorBlender(-10, predictionColor).getScaledValue());
});
});

describe('getPreferenceColorArray()', function () {
it('looks up red colors', function () {
it('just scales value with abs preferenceMin/preferenceMax based on negative/positive values', function () {
let predictionColor = {
'preferenceBins': {
'pos': [1, 8, 10],
'neg': [2, 6, 20]
}
preferenceMax: 2,
preferenceMin: -5,
};
let color = RED_COLOR_NAME;
let colorArray = PREFERENCE_REDS;
assert.equal(colorArray[0], new ColorBlender(0, predictionColor).getPreferenceColorArray(color));
assert.equal(colorArray[1], new ColorBlender(1.1, predictionColor).getPreferenceColorArray(color));
assert.equal(colorArray[1], new ColorBlender(7, predictionColor).getPreferenceColorArray(color));
assert.equal(colorArray[2], new ColorBlender(8, predictionColor).getPreferenceColorArray(color));
assert.equal(colorArray[3], new ColorBlender(11, predictionColor).getPreferenceColorArray(color));
assert.equal(colorArray[0], new ColorBlender(-0.1, predictionColor).getPreferenceColorArray(color));
assert.equal(colorArray[0], new ColorBlender(-1.9, predictionColor).getPreferenceColorArray(color));
assert.equal(colorArray[1], new ColorBlender(-2, predictionColor).getPreferenceColorArray(color));
assert.equal(colorArray[2], new ColorBlender(-6, predictionColor).getPreferenceColorArray(color));
assert.equal(colorArray[3], new ColorBlender(-20, predictionColor).getPreferenceColorArray(color));

assert.equal(5, new ColorBlender(10, predictionColor).getScaledValue());
assert.equal(-2, new ColorBlender(-10, predictionColor).getScaledValue());
});
});


describe('getColor()', function () {
it('works for red and blue combination for prediction data', function () {
let predictionColor = {
Expand All @@ -143,41 +57,19 @@ describe('ColorBlender', function () {
assert.equal('rgb(128,128,255)', new ColorBlender(-0.5, predictionColor, false).getColor());
});


it('works for red and blue combination for preference data', function () {
let predictionColor = {
color1: RED_COLOR_NAME,
color2: BLUE_COLOR_NAME,
'preferenceBins': {
'pos': [1, 8, 10],
'neg': [2, 6, 20]
}
};
let expectedColor = rgbFromArray(PREFERENCE_GRAY);
assert.equal(expectedColor, new ColorBlender(0, predictionColor, true).getColor());
let deepRed = 'rgb(103,0,13)';
assert.equal(deepRed, new ColorBlender(1.0, predictionColor, true).getColor());

expectedColor = rgbFromArray(PREFERENCE_REDS[0]);
assert.equal(expectedColor, new ColorBlender(0.1, predictionColor, true).getColor());
expectedColor = rgbFromArray(PREFERENCE_REDS[0]);
assert.equal(expectedColor, new ColorBlender(0.9, predictionColor, true).getColor());
expectedColor = rgbFromArray(PREFERENCE_REDS[1]);
assert.equal(expectedColor, new ColorBlender(1.0, predictionColor, true).getColor());
expectedColor = rgbFromArray(PREFERENCE_REDS[2]);
assert.equal(expectedColor, new ColorBlender(8.0, predictionColor, true).getColor());
expectedColor = rgbFromArray(PREFERENCE_REDS[3]);
assert.equal(expectedColor, new ColorBlender(11.0, predictionColor, true).getColor());
let zeroGray = 'rgb(190,190,190)';
assert.equal(zeroGray, new ColorBlender(0.0, predictionColor, true).getColor());

// blues have only 5 items in the color array so slot index will be scaled by PREFERRED_PREFERENCE_ARRAY_LENGTH
expectedColor = rgbFromArray(PREFERENCE_BLUES[0]);
assert.equal(expectedColor, new ColorBlender(-0.1, predictionColor, true).getColor());
expectedColor = rgbFromArray(PREFERENCE_BLUES[1]);
assert.equal(expectedColor, new ColorBlender(-2.1, predictionColor, true).getColor());
expectedColor = rgbFromArray(PREFERENCE_BLUES[1]);
assert.equal(expectedColor, new ColorBlender(-6.0, predictionColor, true).getColor());
expectedColor = rgbFromArray(PREFERENCE_BLUES[2]);
assert.equal(expectedColor, new ColorBlender(-20.0, predictionColor, true).getColor());
expectedColor = rgbFromArray(PREFERENCE_BLUES[2]);
assert.equal(expectedColor, new ColorBlender(-40.0, predictionColor, true).getColor());
let midnightBlue = 'rgb(25,25,112)';
assert.equal(midnightBlue, new ColorBlender(-1.0, predictionColor, true).getColor());
});
});

Expand Down
Loading

0 comments on commit 7027f6c

Please sign in to comment.