mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-30 14:28:12 +08:00
Add user-defined category-label colors (#1402)
* Add user-defined category-label colors Fixes https://github.com/chanzuckerberg/cellxgene/issues/1152 As described in https://github.com/chanzuckerberg/cellxgene/issues/1307 * Respond to feedback from @bkmartinjr in nodejs * Respond to feedback from @bkmartinjr in python * Add tests to the server module * Autoformat python, run linter * Make colors_get error handling specific * Respond to feedback from @bkmartinjr * Respond to feedback from @bkmartinjr * Fix whitespace * Fix python lint errrors * Update documentation * Add --disable-user-colors option to launch and cxgtool.py * Fix python formatting * Rename '--disable-user-colors' to '--disable-custom-colors'
This commit is contained in:
@@ -11,13 +11,16 @@ import { range } from "../range";
|
||||
/*
|
||||
create new colors state object. Paramters:
|
||||
- world - current world object
|
||||
- mode - color-by mode. One of: null, "color by expression",
|
||||
"color by continuous metadata", "color by categorical metadata"
|
||||
-
|
||||
- colorMode - color-by mode. One of {null, "color by expression", "color by continuous metadata",
|
||||
"color by categorical metadata"}
|
||||
- colorAccessor - the obs annotations used for color-by
|
||||
*/
|
||||
export function createColors(world, colorMode = null, colorAccessor = null) {
|
||||
export function createColors(world, colorMode = null, colorAccessor = null, userColors = null) {
|
||||
switch (colorMode) {
|
||||
case "color by categorical metadata": {
|
||||
if (userColors && colorAccessor in userColors) {
|
||||
return createUserColors(world, colorAccessor, userColors);
|
||||
}
|
||||
return createColorsByCategoricalMetadata(world, colorAccessor);
|
||||
}
|
||||
case "color by continuous metadata": {
|
||||
@@ -36,8 +39,29 @@ export function createColors(world, colorMode = null, colorAccessor = null) {
|
||||
}
|
||||
}
|
||||
|
||||
function createColorsByCategoricalMetadata(world, accessor) {
|
||||
const { categories } = world.schema.annotations.obsByName[accessor];
|
||||
export function loadUserColorConfig(userColors) {
|
||||
const convertedUserColors = {};
|
||||
Object.keys(userColors).forEach(category => {
|
||||
const [colors, scaleMap] = Object.keys(userColors[category]).reduce((acc, label, i) => {
|
||||
const color = parseRGB(userColors[category][label]);
|
||||
acc[0][label] = color;
|
||||
acc[1][i] = d3.rgb(255 * color[0], 255 * color[1], 255 * color[2]);
|
||||
return acc;
|
||||
}, [{}, {}]);
|
||||
const scale = i => scaleMap[i];
|
||||
convertedUserColors[category] = { colors, scale };
|
||||
});
|
||||
return convertedUserColors;
|
||||
}
|
||||
|
||||
function createUserColors(world, colorAccessor, userColors) {
|
||||
const { colors, scale } = userColors[colorAccessor];
|
||||
const rgb = createRgbArray(world, colors, colorAccessor);
|
||||
return { rgb, scale };
|
||||
}
|
||||
|
||||
function createColorsByCategoricalMetadata(world, colorAccessor) {
|
||||
const { categories } = world.schema.annotations.obsByName[colorAccessor];
|
||||
|
||||
const scale = d3
|
||||
.scaleSequential(interpolateRainbow)
|
||||
@@ -49,14 +73,19 @@ function createColorsByCategoricalMetadata(world, accessor) {
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const rgb = createRgbArray(world, colors, colorAccessor);
|
||||
return { rgb, scale };
|
||||
}
|
||||
|
||||
export function createRgbArray(world, colors, colorAccessor) {
|
||||
const rgb = new Array(world.nObs);
|
||||
const df = world.obsAnnotations;
|
||||
const data = df.col(accessor).asArray();
|
||||
const data = df.col(colorAccessor).asArray();
|
||||
for (let i = 0, len = df.length; i < len; i += 1) {
|
||||
const cat = data[i];
|
||||
rgb[i] = colors[cat];
|
||||
const label = data[i];
|
||||
rgb[i] = colors[label];
|
||||
}
|
||||
return { rgb, scale };
|
||||
return rgb;
|
||||
}
|
||||
|
||||
function createColorsByContinuousMetadata(world, accessor) {
|
||||
|
||||
Reference in New Issue
Block a user