2017-01-25 4 views
3

Я делаю карту тепла в D3 JS с годом вдоль оси X и месяц вдоль оси Y. Каждая ячейка является температурой и на ее основе получает различный «заполняющий» цвет. Мой вопрос в том, как я могу сделать цветовой масштаб, который отображает домен minTemp/maxTemp с рядом цветовых кодов. У меня есть код ниже до сих пор, но это не работает:Как сделать цветную шкалу в D3 JS для использования в атрибуте fill?

var url = "https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/global-temperature.json" 

d3.json(url, function(json){ 

    //load data from API and save in variable data 
    var data = json.monthlyVariance; 
    var baseTemp = json.baseTemperature; 

    //Add temperature to each object in data set 
    for(var i = 0; i < data.length; i++){ 

    var temperature = baseTemp + data[i].variance 
    data[i].temperature = temperature; 

    var monthString = ""; 
    switch(data[i].month){ 

     case 1: 
     data[i].monthString = "January"; 
     break; 
     case 2: 
     data[i].monthString = "February"; 
     break; 
     case 3: 
     data[i].monthString = "March"; 
     break; 
     case 4: 
     data[i].monthString = "April"; 
     break; 
     case 5: 
     data[i].monthString = "May"; 
     break; 
     case 6: 
     data[i].monthString = "June"; 
     break; 
     case 7: 
     data[i].monthString = "July"; 
     break; 
     case 8: 
     data[i].monthString = "August"; 
     break; 
     case 9: 
     data[i].monthString = "September"; 
     break; 
     case 10: 
     data[i].monthString = "October"; 
     break; 
     case 11: 
     data[i].monthString = "November"; 
     break; 
     case 12: 
     data[i].monthString = "December"; 
     break; 
    } 


    } 

    //Set dimensions of div container, svg, and chart area(g element) 
    var margin = {top: 20, right: 40, bottom: 40, left: 80}; 

    //Width of the chart, within SVG element 
    var w = 1000 - margin.left - margin.right; 
    //Height of the chart, within SVG element 
    var h = 500 - margin.top - margin.bottom; 

    //Create SVG element and append to #chart div container 
    var svg = d3.select("#chart") 
       .append("svg") 
       .attr("width", w + margin.left + margin.right) 
       .attr("height", h + margin.top + margin.bottom) 
       .append("g") 
       .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 


    //Get Min Max values 
    var maxYear = d3.max(data, function(d){ 

     return d.year; 

    }); 

    var minYear = d3.min(data, function(d){ 

     return d.year; 

    }); 

    var maxTemp = d3.max(data, function(d){ 

    return d.temperature; 

    });  

    var minTemp = d3.min(data, function(d){ 

    return d.temperature; 

    }) 

    //Create X scale, axis and label 
    var xScale = d3.scaleLinear() 
       .domain([minYear, maxYear]) 
       .range([0,w]); 

    var xAxis = d3.axisBottom() 
       .scale(xScale) 
       .ticks(20) 
       .tickFormat(d3.format("d")); 

    svg.append("g") 
    .attr("class", "axis") 
    .attr("transform", "translate(0," + h + ")") 
    .call(xAxis); 

    //Create Y scale, axis and label 

    var cellHeight = (h/12); 

    var yRange = []; 

    for(var i = 0; i < 12 ; i++){ 

     yRange.push(i * cellHeight); 

    } 

    var yScale = d3.scaleOrdinal() 
       .domain(["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]) 
       .range(yRange); 

    var yAxis = d3.axisLeft() 
       .scale(yScale) 
       .ticks(12); 

    svg.append("g") 
    //append a g element 
    .attr("class", "axis") 
    .call(yAxis) 
     //call yAxis function on this g element 
    .selectAll(".tick text") 
    //select all elements with class tick and nested text element 
    .attr("transform", "translate(0," + (cellHeight/2) + ")"); 
    //move all text elements half a cell height down 

    //Create color scale 
    var colors = d3.scaleOrdinal() 
       .domain([minTemp,maxTemp]) 
       .range(["#5E4FA2", "#3288BD", "#66C2A5", "#ABDDA4", "#E6F598", "#FFFFBF", "#FEE08B", "#FDAE61", "#F46D43", "#D53E4F", "#9E0142"]); 

    //Select all rect elements in G container element, bind data and append 
    var cells = svg.selectAll("cells") 
       .data(data) 
       .enter() 
       .append("rect"); 


    var cellAttributes = cells 
         .attr("x", function(d){ 

          return xScale(d.year); 

         }) 
         .attr("y", function(d){ 

          return yScale(d.monthString); 

         }) 
         .attr("width", w/(maxYear-minYear)) 
         .attr("height", h/12) 
         .attr("fill", function(d){ 

          return colors(d); 

         }) 
         .attr("class", "cell"); 


}); 

Я мог бы написать долго, если/другое заявление, в функции атрибута заливки, для отображения температуры в цветовой код, но это не Думаю, «способ D3». Как это сделать со шкалой ?:

var colors = d3.scaleOrdinal() 
       .domain([minTemp,maxTemp]) 
       .range(["#5E4FA2", "#3288BD", "#66C2A5", "#ABDDA4", "#E6F598", "#FFFFBF", "#FEE08B", "#FDAE61", "#F46D43", "#D53E4F", "#9E0142"]); 
+0

Вы используете порядковый масштаб, поэтому приведенный выше код работает для u.May, я знаю это требование более конкретно. –

ответ

5

Здесь не требуется порядковый масштаб. Вместо этого вам нужна шкала quantize:

Количественные шкалы аналогичны линейным шкалам, за исключением того, что они используют дискретный, а не непрерывный диапазон. Непрерывный входной домен делится на равномерные сегменты на основе количества значений в (то есть мощности) выходного диапазона.

Таким образом, это должно быть ваша шкала:

var colors = d3.scaleQuantize() 
    .domain([minTemp,maxTemp]) 
    .range(["#5E4FA2", "#3288BD", "#66C2A5", "#ABDDA4", "#E6F598", 
    "#FFFFBF", "#FEE08B", "#FDAE61", "#F46D43", "#D53E4F", "#9E0142"]); 

Вот демо:

var data = d3.range(50); 
 

 
var colors = d3.scaleQuantize() 
 
    .domain([0,50]) 
 
    .range(["#5E4FA2", "#3288BD", "#66C2A5", "#ABDDA4", "#E6F598", 
 
    "#FFFFBF", "#FEE08B", "#FDAE61", "#F46D43", "#D53E4F", "#9E0142"]); 
 
\t \t 
 
var svg = d3.select("svg"); 
 

 
var rects = svg.selectAll(".rects") 
 
\t .data(data) 
 
\t .enter() 
 
\t .append("rect") 
 
\t .attr("y", 10) 
 
\t .attr("height", 100) 
 
\t .attr("x", (d,i)=>10 + i*9) 
 
\t .attr("width", 6) 
 
\t .attr("fill", d=>colors(d)) 
 
\t .attr("stroke", "gray");
<script src="https://d3js.org/d3.v4.min.js"></script> 
 
<svg width="500"></svg>

Вы также можете использовать scaleLinear, который имеет преимущество интерполируя между вашими цветами (так, у вас будет больше 11 цветов в вас r цветов). Тем не менее, обратите внимание, чтобы установить одинаковое количество элементов в домене, используя d3.ticks:

d3.ticks(minTemp, maxTemp, 11); 

Вот демо с scaleLinear:

var data = d3.range(50); 
 

 
var colors = d3.scaleLinear() 
 
    .domain(d3.ticks(0, 50, 11)) 
 
    .range(["#5E4FA2", "#3288BD", "#66C2A5", "#ABDDA4", "#E6F598", 
 
    "#FFFFBF", "#FEE08B", "#FDAE61", "#F46D43", "#D53E4F", "#9E0142"]); 
 
\t \t 
 
var svg = d3.select("svg"); 
 

 
var rects = svg.selectAll(".rects") 
 
\t .data(data) 
 
\t .enter() 
 
\t .append("rect") 
 
\t .attr("y", 10) 
 
\t .attr("height", 100) 
 
\t .attr("x", (d,i)=>10 + i*9) 
 
\t .attr("width", 6) 
 
\t .attr("fill", d=>colors(d)) 
 
\t .attr("stroke", "gray");
<script src="https://d3js.org/d3.v4.min.js"></script> 
 
<svg width="500"></svg>

+0

обе произведение !! Благодарю. Но как D3 интерполирует диапазон с помощью scaleLinear()? Знает ли он, что строки в диапазоне - это цветовые коды? – chemook78

+1

Да! Этот алгоритм более умный, чем я! –

+0

thats прохладный! любой способ D3 Я могу получить значения сегментов домена, которые отображаются на цветовые коды? Или я должен просто разделить максимальное значение домена по длине диапазона и работать с этим. Хотите создать легенду – chemook78

0

спасибо так много для помогите, вот как я в конце концов это сделал:

demo: http://codepen.io/chemok78/full/qRXmWX/

var url = "https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/global-temperature.json" 

d3.json(url, function(json) { 

    //load data from API and save in variable data 
    var data = json.monthlyVariance; 
    var baseTemp = json.baseTemperature; 

    var months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; 

    //Add temperature to each object in data set 
    for (var i = 0; i < data.length; i++) { 

    var temperature = baseTemp + data[i].variance 
    data[i].temperature = temperature; 

    var monthString = ""; 
    switch (data[i].month) { 

     case 1: 
     data[i].monthString = "January"; 
     break; 
     case 2: 
     data[i].monthString = "February"; 
     break; 
     case 3: 
     data[i].monthString = "March"; 
     break; 
     case 4: 
     data[i].monthString = "April"; 
     break; 
     case 5: 
     data[i].monthString = "May"; 
     break; 
     case 6: 
     data[i].monthString = "June"; 
     break; 
     case 7: 
     data[i].monthString = "July"; 
     break; 
     case 8: 
     data[i].monthString = "August"; 
     break; 
     case 9: 
     data[i].monthString = "September"; 
     break; 
     case 10: 
     data[i].monthString = "October"; 
     break; 
     case 11: 
     data[i].monthString = "November"; 
     break; 
     case 12: 
     data[i].monthString = "December"; 
     break; 
    } 


    } 

    //Set dimensions of div container, svg, and chart area(g element) 
    var margin = { 
    top: 40, 
    right: 60, 
    bottom: 100, 
    left: 100 
    }; 

    //Width of the chart, within SVG element 
    var w = 1000 - margin.left - margin.right; 
    //Height of the chart, within SVG element 
    var h = 600 - margin.top - margin.bottom; 

    //Create SVG element and append to #chart div container 
    //SVG is nested G element 
    var svg = d3.select("#chart") 
    .append("svg") 
    .attr("width", w + margin.left + margin.right) 
    .attr("height", h + margin.top + margin.bottom) 
    .append("g") 
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 


    //Get Min Max values 
    var maxYear = d3.max(data, function(d) { 

    return d.year; 

    }); 

    var minYear = d3.min(data, function(d) { 

    return d.year; 

    }); 


    var maxTemp = d3.max(data, function(d) { 

    return d.temperature; 

    }); 

    var minTemp = d3.min(data, function(d) { 

    return d.temperature; 

    }) 


    //Create X scale, axis and label 
    var xScale = d3.scaleLinear() 
    .domain([minYear, maxYear]) 
    .range([0, w]); 

    var xAxis = d3.axisBottom() 
    .scale(xScale) 
    .ticks(20) 
    .tickFormat(d3.format("d")); 

    svg.append("g") 
    .attr("class", "axis") 
    .attr("transform", "translate(0," + h + ")") 
    .call(xAxis); 

    var xLabel = svg.append("text") 
    .text("Year") 
    .attr("x", w/2) 
    .attr("y", h + (margin.bottom/2.5)) 
    .attr("font-size", "14px"); 

    //Create Y scale, axis and label 

    var cellHeight = (h/12); 

    var yRange = []; 

    for (var i = 0; i < 12; i++) { 

    yRange.push(i * cellHeight); 

    } 


    var yScale = d3.scaleOrdinal() 
    .domain(months) 
    .range(yRange); 

    var yAxis = d3.axisLeft() 
    .scale(yScale) 
    .ticks(12); 

    svg.append("g") 
    //append a g element 
    .attr("class", "axis") 
    .call(yAxis) 
    //call yAxis function on this g element 
    .selectAll(".tick text") 
    //select all elements with class tick and nested text element 
    .attr("transform", "translate(0," + (cellHeight/2) + ")"); 
    //move all text elements half a cell height down 

    var yLabel = svg.append("text") 
    .attr("transform", "rotate(-90)") 
    .attr("x", 0 - (h/2)) 
    .attr("y", 0 - (margin.left/1.8)) 
    .style("font-size", "14px") 
    .style("text-anchor", "middle") 
    .text("Month"); 


    //Create color scale 

    var colorCodes = ["#5E4FA2", "#3288BD", "#66C2A5", "#ABDDA4", "#E6F598", "#FFFFBF", "#FEE08B", "#FDAE61", "#F46D43", "#D53E4F", "#9E0142"]; 

    var colors = d3.scaleQuantile() 
    //quantize scale divides domain in bands according to ordinal scale range 
    .domain([minTemp, maxTemp]) 
    //.domain(d3.ticks(minTemp,maxTemp,11)) 
    .range(colorCodes); 

    var colorQuantiles = colors.quantiles(); 
    colorQuantiles.unshift(0); 
    //save the upper ranges of each temperature quantile + 0 at the beginning (quantile function does not count 0 as start) 


    //Append tooltip to chart area. Fully transparant at first 
    var tip = d3.select("#chart").append("div") 
    .attr("class", "tooltip") 
    .style("opacity", 0); 

    //Select all rect elements in G container element, bind data and append 
    var cells = svg.selectAll("cells") 
    .data(data) 
    .enter() 
    .append("rect"); 

    var cellAttributes = cells 
    .attr("x", function(d) { 

     return xScale(d.year); 

    }) 
    .attr("y", function(d) { 

     return yScale(d.monthString); 

    }) 
    .attr("width", w/(maxYear - minYear)) 
    .attr("height", cellHeight) 
    .attr("fill", function(d) { 

     return colors(d.temperature); 

    }) 
    .attr("class", "cell") 
    .on("mouseover", function(d) { 

     tip.transition() 
     .style("opacity", 0.7); 
     tip.html("<strong>" + months[d.month - 1] + " - " + d.year + "</strong><br>" + d.temperature.toFixed(2) + " °C<br>" + d.variance.toFixed(2) + " °C") 
     .style("left", d3.event.pageX + "px") 
     .style("top", d3.event.pageY - 70 + "px"); 

    }) 
    .on("mouseout", function(d) { 

     tip.transition() 
     .style("opacity", 0); 

    }) 

    //Create a legend 

    var blockWidth = 35; 
    var blockHeight = 20; 

    var legend = svg.selectAll(".legend") 
    .data(colorQuantiles) 
    .enter() 
    .append("g") 
    .attr("class", "legend") 
    .attr("font-size", "14px") 
    .attr("font-style", "PT Sans") 
    .attr("transform", function(d, i) { 

     return ("translate(" + i * blockWidth + ",0)") 

    }); 

    legend.append("rect") 
    .attr("x", (w/5) * 3) 
    .attr("y", h + (margin.bottom/3)) 
    .attr("width", blockWidth) 
    .attr("height", blockHeight) 
    .style("fill", function(d, i) { 

     return (colorCodes[i]); 

    }); 

    legend.append("text") 
    .attr("x", ((w/5) * 3) + (blockWidth/2)) 
    .attr("y", (h + (margin.bottom/3)) + blockHeight + 15) 
    .text(function(d, i) { 

     return colorQuantiles[i].toFixed(1); 

    }) 
    .style("text-anchor", "middle"); 

})