2012-08-31 19 views
9

Sto implementando una heatmap in cui il colore di sfondo della cella è determinato da una scala di colori d3. Alcuni dei valori sono categoriali; il loro valore può essere di N diverse categorie di stringhe arbitrarie come ["6TH", "7TH", "5TH", "4TH"].Scala di colori continua dal dominio discreto delle stringhe?

Dato un colore iniziale d3.rgb ("blu") e un colore finale d3.rgb ("rosso"), come posso costruire una scala di colori che mappa un dominio discreto di stringhe in un intervallo di colori continuo?

Ho provato

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

che ovviamente non funziona.

risposta

29

Innanzitutto, vorrei prendere in considerazione l'utilizzo di una delle bilance Colorbrewer prontamente disponibili; vedi colorbrewer2.org. Questi sono anche disponibili come file JavaScript e CSS nel repository git di D3; vedi lib/colorbrewer. Ad esempio, se si dispone di quattro valori discreti nel dominio, e si desidera una scala di rosso-blu divergente, si potrebbe dire:

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

(avrete bisogno di un <script src="colorbrewer.js"></script> da qualche parte prima di questo, anche.) ColorBrewer ha una varietà di scale cromatiche sequenziali, divergenti e categoriali ben progettate.

Se si insiste a ruotare la propria scala di colori, si consiglia vivamente di interpolare in L*a*b* or HCL color space per una percezione accurata. È possibile farlo utilizzando d3.interpolateLab o d3.interpolateHcl. Ad esempio, d3.interpolateLab("red", "blue")(.5) restituisce un colore a metà tra il rosso e il blu.

Per calcolare i colori per l'intervallo della scala ordinale, è possibile utilizzare un interpolatore oppure è possibile trovare una scala lineare temporanea più comoda.Ad esempio:

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

: La lib di Colorbrewer di D3 è stata spostata nel repository [d3-scale-chromatic] (https://github.com/d3/d3-scale-chromatic). – danasilver

3

Hai l'idea giusta, devi solo elaborare ogni canale di colore R/G/B. Per esempio, in vaniglia JavaScript è possibile effettuare le seguenti operazioni:

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(); 
} 

Secondo il d3 documentation, è possibile estrarre ogni canale di colore utilizzando i r, g e b attributi di un oggetto di colore:

# d3.rgb(color)

Costruisce un nuovo colore RGB analizzando la stringa di colore specificata. Se il colore non è una stringa, è forzato a una stringa; quindi, questo costruttore può anche essere usato per creare una copia di un colore esistente o forzare la conversione di un colore d3.hsl in RGB.

...

Il colore risultante viene memorizzato come valori interi canali rosso, verde e blu nell'intervallo [0255]. I canali sono disponibili come attributi r, geb dell'oggetto restituito.

Così in cima l'esempio precedente, si potrebbe dire:

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

fa questo aiuto?

+0

Sì, è certamente aiutato! Grazie, funziona perfettamente. Aggiornamento – ekerner

0

È sempre possibile concatenare una scala ordinale e una scala lineare.

La prima scala creerà valori quantificabili dai valori discreti e la seconda scala interpolerà questi valori su una scala di colori.

Qualcosa di simile a questo:

// 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>

Problemi correlati