2012-08-31 29 views
9

Estoy implementando un mapa de calor en el que el color de fondo de la celda está determinado por una escala de color d3. Algunos de los valores son categóricos; su valor puede ser de N diferentes categorías arbitrarias de tipo de cadena como ["6TH", "7TH", "5TH", "4TH"].¿Escala de color continua del dominio discreto de las cadenas?

Dado un color de inicio d3.rgb ("azul") y un color final d3.rgb ("rojo"), ¿cómo puedo construir una escala de colores que mapee un dominio discreto de cadenas en un rango de color continuo?

me trataron

var scale = d3.scale.ordinal() 
    .domain(["6TH", "7TH", "5TH", "4TH"]) 
    .rangeBands([ d3.rgb("blue"), d3.rgb("red") ]); 

que obviamente no funciona.

Respuesta

29

En primer lugar, consideraría utilizar una de las básculas Colorbrewer disponibles; ver colorbrewer2.org. Estos también están disponibles como archivos JavaScript y CSS en el repositorio git de D3; ver lib/colorbrewer. Por ejemplo, si tiene cuatro valores discretos en su dominio, y desea una escala divergente rojo-azul, se podría decir:

var color = d3.scale.ordinal() 
    .domain(["6TH", "7TH", "5TH", "4TH"]) 
    .range(colorbrewer.RdBu[4]); 

(usted necesitará un <script src="colorbrewer.js"></script> algún lugar antes de esto, también.) ColorBrewer tiene una variedad de escalas de color secuenciales, divergentes y categóricas bien diseñadas.

Si insiste en hacer rodar su propia escala de colores, le recomiendo interpolar en L*a*b* or HCL color space para una percepción precisa. Puede hacerlo usando d3.interpolateLab o d3.interpolateHcl. Por ejemplo, d3.interpolateLab("red", "blue")(.5) devuelve un color a medio camino entre rojo y azul.

Para calcular los colores para el rango de su escala ordinal, puede usar un interpolador, o puede encontrar una escala lineal temporal más conveniente.Por ejemplo:

var categories = ["6TH", "7TH", "5TH", "4TH"]; 

var color = d3.scale.ordinal() 
    .domain(categories) 
    .range(d3.range(categories.length).map(d3.scale.linear() 
     .domain([0, categories.length - 1]) 
     .range(["red", "blue"]) 
     .interpolate(d3.interpolateLab))); 
+1

Actualización: Colorbrewer lib de D3 se ha movido al repositorio [d3-scale-chromatic] (https://github.com/d3/d3-scale-chromatic). – danasilver

3

Tiene la idea correcta, solo necesita procesar cada canal de color R/G/B. Por ejemplo, en JavaScript básico se puede hacer lo siguiente:

var a = [255, 0, 0], // First color 
    b = [0, 0, 255], // Other color 
    bands = 5,  // Bands is the length of your domain 
    i, 
    delta = [];  // Difference between color in each channel 

// Compute difference between each color 
for (i = 0; i < 4; i++){ 
    delta[i] = (a[i] - b[i])/(bands + 1); 
} 

// Use that difference to create your bands 
for (i = 0; i <= bands + 1; i++){ 
    var r = Math.round(a[0] - delta[0] * i); 
    var g = Math.round(a[1] - delta[1] * i); 
    var b = Math.round(a[2] - delta[2] * i); 
    console.log("<div style='background-color: #" + dec2hex(r) + dec2hex(g) + dec2hex(b) + "'>Band " + i + "</div>"); 
} 

// A helper function for formatting 
function dec2hex(i) { 
    return (i+0x100).toString(16).substr(-2).toUpperCase(); 
} 

De acuerdo con la d3 documentation, puede extraer cada canal de color utilizando los r, g y b atributos de un objeto de color:

# d3.rgb(color)

Construye un nuevo color RGB al analizar la cadena de color especificada. Si el color no es una cadena, se fuerza a una cadena; por lo tanto, este constructor también se puede usar para crear una copia de un color existente o forzar la conversión de un color d3.hsl a RGB.

...

El color resultante se almacena como valores de canal número entero de color rojo, verde y azul en el intervalo [0255]. Los canales están disponibles como los atributos r, g y b del objeto devuelto.

Así que en la parte superior del ejemplo anterior, se podría decir:

var myColor = d3.rgb("blue"), 
    a = [myColor.r, myColor.g, myColor.b], 
... 

ayuda eso?

+0

Sí, es sin duda ayudó! Gracias, trabaja perfectamente. – ekerner

0

Siempre puede encadenar una escala ordinal y una escala lineal.

La primera escala creará valores cuantificables a partir de sus valores discretos, y la segunda escala interpolará estos valores en una escala de colores.

Algo como esto:

// Your categories 
 
var data = ["6TH", "7TH", "5TH", "4TH"], 
 

 
    // Define ordinal to linear scale... 
 
    ordinal = d3.scale.ordinal().domain(data).rangePoints([0, 1]), 
 

 
    // ...and linear to color scale 
 
    linear = d3.scale.linear().domain([0, 1]).range([d3.rgb("blue"), d3.rgb("red")]); 
 

 
// Now define your artificial 'compound' scale 
 
function scale(d) { 
 
    return linear(ordinal(d)); 
 
} 
 

 
// And then use it on your D3 code 
 
d3.selectAll('div') 
 
    .data(data) 
 
    .enter() 
 
    .append('div') 
 
    .style('background', scale) // <- et voilà ;) 
 
    .text(function(d) { 
 
    return d; 
 
    });
div { 
 
    color: white; 
 
    width: 3em; 
 
    padding: 1em; 
 
    margin: .2em 
 
    text-align: center; 
 
    font-family: Arial, Helvetica, sans-serif; 
 
    font-weight: bold 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Cuestiones relacionadas