Как бы заставить тики быть выровненными по оси Y, а текст тика располагался бы в центре каждого тика по вертикали в d3. js? - PullRequest
0 голосов
/ 07 апреля 2020

В настоящее время мои галочки выравниваются следующим образом: issue

И я бы хотел, чтобы они были выровнены таким образом, а текст выровнен по вертикали следующим образом: solution

Я пробовал несколько разных вариантов, где визуально он был бы выровнен, но не проходит тесты, так как идентификаторы каждой отдельной ячейки не выровнены со значениями на оси Y. Я не уверен, как бы я это сделал. Вот моя кодовая ссылка

const addMonthStrings = dataset => {
  for (let i=0; i < dataset.length; i++) {
    let newPropName = 'monthStr';
    switch (dataset[i]['month']) {
      case 1:
        dataset[i][newPropName] = 'January';
        break;
      case 2:
        dataset[i][newPropName] = 'February';
        break;
      case 3:
        dataset[i][newPropName] = 'March';
        break;
      case 4:
        dataset[i][newPropName] = 'April';
        break;
      case 5:
        dataset[i][newPropName] = 'May';
        break;
      case 6:
        dataset[i][newPropName] = 'June';
        break;
      case 7:
        dataset[i][newPropName] = 'July';
        break;
      case 8:
        dataset[i][newPropName] = 'August';
        break;
      case 9:
        dataset[i][newPropName] = 'September';
        break;
      case 10:
        dataset[i][newPropName] = 'October';
        break;
      case 11:
        dataset[i][newPropName] = 'November';
        break;
      case 12:
        dataset[i][newPropName] = 'December';
        break;
    };
  };
};

const tempColoring = (currTemp, colorPalette) => {
  if (currTemp <= 2.8) {
    return colorPalette.color1;
  } else if (currTemp > 2.8 && currTemp <= 3.9) {
    return colorPalette.color2;
  } else if (currTemp > 3.9 && currTemp <= 5.0) {
    return colorPalette.color3;
  } else if (currTemp > 5.0 && currTemp <= 6.1) {
    return colorPalette.color4;
  } else if (currTemp > 6.1 && currTemp <= 7.2) {
    return colorPalette.color5;
  } else if (currTemp > 7.2 && currTemp <= 8.3) {
    return colorPalette.color6;
  } else if (currTemp > 8.3 && currTemp <= 9.5) {
    return colorPalette.color7;
  } else if (currTemp > 9.5 && currTemp <= 10.6) {
    return colorPalette.color8;
  } else if (currTemp > 10.6 && currTemp <= 11.7) {
    return colorPalette.color9;
  } else if (currTemp > 11.7 && currTemp <= 12.8) {
    return colorPalette.color10;
  } else {
    return colorPalette.color11;
  }
};

const render = (baseTemperature, dataset) => {
  const width = 960;
  const height = 500;
  
  const xValue = d => d['year'];
  const yValue = d => d['month'] - 1;
  const monthStr = d => d['monthStr'];
  const variance = d => Math.round(d['variance'] * 10) / 10;
  const currTemp = d => Math.round((baseTemperature + d['variance']) * 10) / 10;
  
  const margin = { top: 110, right: 60, bottom: 120, left: 100 };
  const innerWidth = width - margin.left - margin.right;
  const innerHeight = height - margin.top - margin.bottom;
  
  const minYear = d3.min(dataset, xValue);
  const maxYear = d3.max(dataset, xValue);
  const yearRange = maxYear - minYear;
  
  const minMonth = d3.min(dataset, yValue);
  const maxMonth = d3.max(dataset, yValue);
  
  const cellWidth = innerWidth / yearRange;
  const cellHeight = innerHeight / 12;
  
  const xAxisLabelText = 'Years';
  const xAxisLabelXPos = innerWidth / 2;
  const xAxisLabelYPos = 40;
  
  const yAxisLabelText = 'Months';
  const yAxisLabelXPos = -innerHeight / 2;
  const yAxisLabelYPos = -70;
  
  const titleText = 'Monthly Global Land-Surface Temperature';
  const titleXAxisPos = width / 2;
  const titleYAxisPos = 40;
  
  const subtitleText = `${minYear} - ${maxYear}: base temperature ${baseTemperature}℃`;
  const subtitleYAxisPos = titleYAxisPos + 22;
  
  const colorPalette = {
    'color1': 'rgb(49, 54, 149)',
    'color2': 'rgb(69, 117, 180)',
    'color3': 'rgb(116, 173, 209)',
    'color4': 'rgb(171, 217, 233)',
    'color5': 'rgb(224, 243, 248)',
    'color6': 'rgb(255, 255, 191)',
    'color7': 'rgb(254, 224, 144)',
    'color8': 'rgb(253, 174, 97)',
    'color9': 'rgb(244, 109, 67)',
    'color10': 'rgb(215, 48, 39)',
    'color11': 'rgb(165, 0, 38)'
  }; // Colors will be assigned correspondingly to the increase in temp (1 - coldest, 11 - warmest)
  
  // Initiate a svg canvas
  const svg = d3.select('body')
    .append('svg')
    .style('height', height)
    .style('width', width)
  
  // Initiate a heat map
  const heatmap = svg.append('g')
    .attr('transform', `translate(${margin.left}, ${margin.top})`);
  
  // Establish a scale
  const xScale = d3.scaleTime()
    .domain([minYear - 1, maxYear])
    .range([0, innerWidth]);
  
  const yScale = d3.scaleTime()
    .domain([minMonth, maxMonth])
    .range([0, innerHeight]);
  
  // Create axes
  const xAxis = d3.axisBottom(xScale).tickFormat(d => d3.timeFormat('%Y')( new Date(0).setFullYear(d) ));
  const yAxis = d3.axisLeft(yScale).tickFormat(d => d3.timeFormat('%B')( new Date(0).setMonth(d) ));
  
  const xAxisG = heatmap.append('g')
       .attr('id', 'x-axis')
       .attr('transform', `translate(0, ${innerHeight})`)
       .call(xAxis);
  
  xAxisG.append('text')
    .attr('id', 'xAxis-label')
    .attr('x', xAxisLabelXPos)
    .attr('y', xAxisLabelYPos)
    .attr('fill', 'black')
    .attr('text-anchor', 'middle')
    .style('font-size', '1.5em')
    .text(xAxisLabelText)
    
  const yAxisG = heatmap.append('g')
    .attr('id', 'y-axis')
    .attr('transform', `translate(0, 0)`)
    .call(yAxis);
  
  yAxisG.append('text')
    .attr('id', 'yAxis-label')
    .attr('x', yAxisLabelXPos)
    .attr('y', yAxisLabelYPos)
    .attr('fill', 'black')
    .attr('transform', 'rotate(-90)')
    .attr('text-anchor', 'middle')
    .style('font-size', '1.5em')
    .text(yAxisLabelText)
  
  const titleSection = svg.append('g')
      .attr('text-anchor', 'middle')
    
  titleSection.append('text')
    .attr('id', 'title')
    .attr('x',  titleXAxisPos)
    .attr('y', titleYAxisPos)
    .style('font-size', '1.5em')
    .style('font-weight', 'bold')
    .text(titleText);
    
  titleSection.append('text')
    .attr('id', 'description')
    .attr('x', titleXAxisPos)
    .attr('y', subtitleYAxisPos)
    .style('font-size', '1.2em')
    .text(subtitleText);
  
  let tooltip = d3.select('body').append('div')
    .attr('id', 'tooltip')
    .style('opacity', 0)
  
  // Append cells to the heatmap
  heatmap.selectAll('rect').data(dataset)
    .enter().append('rect')
    .attr('x', d => xScale(xValue(d)))
    .attr('y', d => yScale(yValue(d)) - cellHeight)
    .attr('width', cellWidth)
    .attr('height', cellHeight)
    .attr('fill', d => tempColoring(currTemp(d), colorPalette)) //fill with palette accordingly
    .attr('class', 'cell')
    .attr('data-year', d => xValue(d))
    .attr('data-month', d => yValue(d))
    .attr('data-temp', d => currTemp(d))
    .on('mouseover', d => {
      tooltip.transition().duration(200).style('opacity', 0.9);
      tooltip.html(`${xValue(d)} - ${monthStr(d)}
                    </br>${currTemp(d)}°C
                    </br>${variance(d)}°C`)
        .style('left', d3.event.pageX + 'px')
        .style('top', d3.event.pageY + 'px')
        .attr('data-year', xValue(d));
    })
    .on('mouseout', d => {
      tooltip.transition().duration(500).style('opacity', 0);
    });
  
  /*
  const legendWidth = 200;
  const legendSquareWidth = legendWidth / 11;
  
  const legend = svg.append('g')
    .attr('transform', `translate(${margin.left}, ${margin.top + 80})`);
  
  const tempScale = d3.scaleLinear()
      .domain([d3.min(dataset, currTemp), d3.max(dataset, currTemp)])
      .range([0, legendWidth]);
  
  const xAxisLegend = d3.axisBottom(tempScale);
  
  legend.append('g')
       .attr('id', 'legend-x-axis')
       .attr('transform', `translate(0, ${innerHeight})`)
       .call(xAxisLegend);
  /*
  legend.selectAll('rect').data(dataset)
       .enter().append('rect')
       .attr('x', (d, i) => i)
       .attr('y', d => yScale(yValue(d)))
       .attr('width', 10)
       .attr('height', 20) */
};

document.addEventListener('DOMContentLoaded', function() {
  const request = new XMLHttpRequest();
  request.open('GET', 'https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/global-temperature.json', true);
  request.send();
  request.onload = function () {
    let json = JSON.parse(request.responseText);
    let baseTemperature = json.baseTemperature;
    let dataset = json.monthlyVariance;
    addMonthStrings(dataset);
    render(baseTemperature, dataset);
  };
});
body {
  background-color: rgb(229, 226, 224);
  font-family: monospace;
}

svg {
  background-color: white;
  position: absolute;
  top: 50%;
  left: 50%;
  -moz-transform: translateX(-50%) translateY(-50%);
  -webkit-transform: translateX(-50%) translateY(-50%);
  transform: translateX(-50%) translateY(-50%);
}

#tooltip {
  position: relative;
  text-align: center;
  width: 120px;
  border: 1px solid rgb(97, 94, 84);
  border-radius: 10px;
  background: rgb(229, 226, 224);
  padding: 10px;
}

.cell:hover {
  stroke: black;
  stroke-width: 0.8;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.0.0/d3.min.js"></script>
<script src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"></script>

<body></body>
...