Тепловая карта календаря: цвет не назначен

Я создал тепловую карту календаря, но проблема в том, что данные, которые попадают в тормоз «более 4500», помечены черным цветом вместо указанного цвета #3d3768. Похоже, что цвет не может быть найден для этой категории. Почему?

<!DOCTYPE html>
<meta charset="utf-8">
<head>
<title>Data Calendar</title>
<style>
    .month {
        fill: none;
        stroke: #000;
        stroke-width: 2px;
    }
    .day {
        fill: #fff;
        stroke: #ccc;
    }
    text    {
        font-family:sans-serif;
        font-size:1.5em;
    }
    .dayLabel   {
        fill:#aaa;
        font-size:0.8em;
    }
    .monthLabel {
        text-anchor:middle;
        font-size:0.8em;
        fill:#aaa;
    }
    .yearLabel  {
        fill:#aaa;
        font-size:1.2em;
    }

    .key    {font-size:0.5em;}

</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<script>

var breaks=[1500,2500,3500,4500,4550];
var colours=["#e2dff1","#b7b1dd","#8b82c8","#584f95","#3d3768"];

    //general layout information
    var cellSize = 17;
    var xOffset=20;
    var yOffset=60;
    var calY=50;//offset of calendar in each group
    var calX=25;
    var width = 960;
    var height = 163;
    var parseDate = d3.time.format("%d/%m/%y").parse;
    format = d3.time.format("%d-%m-%Y");
    toolDate = d3.time.format("%d/%b/%y");

    d3.csv("data.csv", function(error, data) {

        //set up an array of all the dates in the data which we need to work out the range of the data
        var dates = new Array();
        var values = new Array();

        //parse the data
        data.forEach(function(d)    {
                dates.push(parseDate(d.date));
                values.push(d.value);
                d.date=parseDate(d.date);
                d.value=d.value;
                d.year=d.date.getFullYear();//extract the year from the data
        });

        var yearlyData = d3.nest()
            .key(function(d){return d.year;})
            .entries(data);

        var svg = d3.select("body").append("svg")
            .attr("width","90%")
            .attr("viewBox","0 0 "+(xOffset+width)+" 540")

        //title
        svg.append("text")
        .attr("x",xOffset)
        .attr("y",20)
        .text(title);

        //create an SVG group for each year
        var cals = svg.selectAll("g")
            .data(yearlyData)
            .enter()
            .append("g")
            .attr("id",function(d){
                return d.key;
            })
            .attr("transform",function(d,i){
                return "translate(0,"+(yOffset+(i*(height+calY)))+")";  
            })

        var labels = cals.append("text")
            .attr("class","yearLabel")
            .attr("x",xOffset)
            .attr("y",15)
            .text(function(d){return d.key});

        //create a daily rectangle for each year
        var rects = cals.append("g")
            .attr("id","alldays")
            .selectAll(".day")
            .data(function(d) { return d3.time.days(new Date(parseInt(d.key), 0, 1), new Date(parseInt(d.key) + 1, 0, 1)); })
            .enter().append("rect")
            .attr("id",function(d) {
                return "_"+format(d);
                //return toolDate(d.date)+":n"+d.value+" dead or missing";
            })
            .attr("class", "day")
            .attr("width", cellSize)
            .attr("height", cellSize)
            .attr("x", function(d) {
                return xOffset+calX+(d3.time.weekOfYear(d) * cellSize);
            })
            .attr("y", function(d) { return calY+(d.getDay() * cellSize); })
            .datum(format);

        //create day labels
        var days = ['Su','Mo','Tu','We','Th','Fr','Sa'];
        var dayLabels=cals.append("g").attr("id","dayLabels")
        days.forEach(function(d,i)    {
            dayLabels.append("text")
            .attr("class","dayLabel")
            .attr("x",xOffset)
            .attr("y",function(d) { return calY+(i * cellSize); })
            .attr("dy","0.9em")
            .text(d);
        })

        //let's draw the data on
        var dataRects = cals.append("g")
            .attr("id","dataDays")
            .selectAll(".dataday")
            .data(function(d){
                return d.values;   
            })
            .enter()
            .append("rect")
            .attr("id",function(d) {
                return format(d.date)+":"+d.value;
            })
            .attr("stroke","#ccc")
            .attr("width",cellSize)
            .attr("height",cellSize)
            .attr("x", function(d){return xOffset+calX+(d3.time.weekOfYear(d.date) * cellSize);})
            .attr("y", function(d) { return calY+(d.date.getDay() * cellSize); })
            .attr("fill", function(d) {
                if (d.value<breaks[0]) {
                    return colours[0];
                }
                for (i=0;i<breaks.length+1;i++){
                    if (d.value>=breaks[i]&&d.value<breaks[i+1]){
                        return colours[i];
                    }
                }
                if (d.value>breaks.length-1){
                    return colours[breaks.length]   
                }
            })

        //append a title element to give basic mouseover info
        dataRects.append("title")
            .text(function(d) { return toolDate(d.date)+":n"+d.value+units; });

        //add montly outlines for calendar
        cals.append("g")
        .attr("id","monthOutlines")
        .selectAll(".month")
        .data(function(d) { 
            return d3.time.months(new Date(parseInt(d.key), 0, 1),
                                  new Date(parseInt(d.key) + 1, 0, 1)); 
        })
        .enter().append("path")
        .attr("class", "month")
        .attr("transform","translate("+(xOffset+calX)+","+calY+")")
        .attr("d", monthPath);

        //retreive the bounding boxes of the outlines
        var BB = new Array();
        var mp = document.getElementById("monthOutlines").childNodes;
        for (var i=0;i<mp.length;i++){
            BB.push(mp[i].getBBox());
        }

        var monthX = new Array();
        BB.forEach(function(d,i){
            boxCentre = d.width/2;
            monthX.push(xOffset+calX+d.x+boxCentre);
        })

        //create centred month labels around the bounding box of each month path
        //create day labels
        var months = ['JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP','OCT','NOV','DEC'];
        var monthLabels=cals.append("g").attr("id","monthLabels")
        months.forEach(function(d,i)    {
            monthLabels.append("text")
            .attr("class","monthLabel")
            .attr("x",monthX[i])
            .attr("y",calY/1.2)
            .text(d);
        })

         //create key
        var key = svg.append("g")
            .attr("id","key")
            .attr("class","key")
            .attr("transform",function(d){
                return "translate("+xOffset+","+(yOffset-(cellSize*1.5))+")";
            });

        key.selectAll("rect")
            .data(colours)
            .enter()
            .append("rect")
            .attr("width",cellSize)
            .attr("height",cellSize)
            .attr("x",function(d,i){
                return i*130;
            })
            .attr("fill",function(d){
                return d;
            });

        key.selectAll("text")
            .data(colours)
            .enter()
            .append("text")
            .attr("x",function(d,i){
                return cellSize+5+(i*130);
            })
            .attr("y","1em")
            .text(function(d,i){
                if (i<colours.length-1){
                    return "up to "+breaks[i] + "%";
                }   else    {
                    return "over "+breaks[i-1] + "%";   
                }
            });

    });//end data load

    //pure Bostock - compute and return monthly path data for any year
    function monthPath(t0) {
      var t1 = new Date(t0.getFullYear(), t0.getMonth() + 1, 0),
          d0 = t0.getDay(), w0 = d3.time.weekOfYear(t0),
          d1 = t1.getDay(), w1 = d3.time.weekOfYear(t1);
      return "M" + (w0 + 1) * cellSize + "," + d0 * cellSize
          + "H" + w0 * cellSize + "V" + 7 * cellSize
          + "H" + w1 * cellSize + "V" + (d1 + 1) * cellSize
          + "H" + (w1 + 1) * cellSize + "V" + 0
          + "H" + (w0 + 1) * cellSize + "Z";
    }

</script>
</body>
</html>

1 ответ

  1. Ваш код выбора цвета может быть несколько уменьшен в размере, и использование шкалы d3 для выбора вашего цвета сделает это намного чище, а также. Но, в конечном счете, ваша проблема лежит здесь:

                if (d.value>breaks.length-1){
                    return colours[breaks.length]   
                }
    

    Этот раздел кода вызывается только тогда, когда цвет еще не возвращен, то есть для тех значений, которые больше 4550. Хотя я не уверен, почему оператор if в этом блоке кода необходим, ваши массивы breaksи coloursимеют одинаковую длину. По существу, вы возвращаетеarray[array.length], который не будет возвращать значение, поскольку массивы являются нулевыми индексированными. Вы, вероятно, хотите вернутьсяcolours[colours.length-1], как вы хотите последний указанный цвет.


    Edit: кроме того, если вы назначите цвет для тех, кто ниже вашего самого низкого разрыва (что, как представляется, вы делаете), и тех, кто выше вашего самого высокого, вам понадобится один цвет больше, чем значение разрыва. Например: одна шкала перерыва имеет два цвета для тех, кто выше / ниже перерыва. Тем не менее, возможно, вы намеренно применили colours[0]оба: значения под breaks[0]и значения между breaks[0]и breaks[1]:

                if (d.value<breaks[0]) {
                    return colours[0];
                }
                for (i=0;i<breaks.length+1;i++){
                    if (d.value>=breaks[i]&&d.value<breaks[i+1]){
                        return colours[i];
                    }
                }