d3.js检查/计数系列图表 [英] d3.js Checking/Count series chart

查看:116
本文介绍了d3.js检查/计数系列图表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个使用foursquare数据的应用程序。



//这是有一些深入研究的系列图表 - 但仍然存在一些错误在这里跑来跑去。



所以我们有一批数据 - Health&美容,餐厅,咖啡厅,公共房屋。
- 会有COUNT个 - 以及结帐信息的摘要。
所以我希望这个图表能够显示场地的数量,但也表明它们是多么流行..所以例如酒吧的数量可能会更小,但是因为它们更受欢迎,所以签到的数量更多。因此,在这种情况下,想要反转圆圈的颜色。



目前的代码尝试存在一些错误。




  • 圆圈/圆圈间距的交换会导致黑色路径中的眼泪和奇怪的行为

  • 我希望在下面有一条黑线。蓝色圆圈,但在蓝色圆圈内显示一个裁剪的圆圈路径橙色线 - 所以一种掩蔽能力。





我的脚本确保较小的圆圈始终位于愤怒一个,所以两个圆圈总是可见的。
在这里你可以找到我的代码:



 <!DOCTYPE HTML>< HEAD> < meta charset =utf-8> < script src =https://d3js.org/d3.v5.min.js>< / script> <风格> html {font-family:sans-serif; }< / style>< / head>< body> < div id =container> < / DIV> <脚本> const data = [{name:Twitter,vists:150亿,unit:每天,layer1value:15000000,layer2value:450},{name: Facebook,vists:50亿,单位:每天,layer1value:4000000,layer2value:5000000},{name:Google,vists:50亿 ,unit:每天,layer1value:5000000,layer2value:25000},{name:Netflix,vists:100亿,unit:每天 ,layer1value:3000000,layer2value:2200},{name:Ebay,vists:80亿,unit:每天,layer1value:2500000,layer2value :4900000},{name:Klout,vists:20亿,unit:每天,layer1value:1000000,layer2value:45}]; / * *查找最大和最小层大小* / const值= data.reduce((acumulator,datum)=> [... acumulator,datum.layer1value,datum.layer2value],[]); const maxValue = Math.max(... values); const minValue = Math.min(... values); / * *基于最小和最大的layer1value或layer2value * / const radiusScale = d3.scaleLinear()。域([minValue,maxValue])。范围([10,150])创建比例; //圆周宽度的最小值和最大值= 900; const height = 500; const orangeColour ='#ffb000'; const blueColour ='#00a1ff'; //创建svg元素const svg = d3.select('#container')。append('svg')。attr('width',width).attr('height',height);让xPos = 0; //圆圈的位置/ * *遍历每个数据并渲染所有相关元素:两个圆圈和两个带指针线的标签* / for(let i = 0; i< data.length; i ++){const d = data [一世]; //当前数据点const currMaxRadius = radiusScale(Math.max(d.layer1value,d.layer2value)); //在两层当前组中得到最大半径xPos + = currMaxRadius; //将该半径添加到xPos //创建包含当前基准的所有视图元素的组元素const group = svg.append('g')。attr('transform',`translate($ {xPos},$ {height / 2 })`); group.append('circle')。attr('r',radiusScale(d.layer1value))。style('fill',blueColour); group.insert('circle',d.layer2value> d.layer1value?':first-child':null)//如果layer2value大于layer1value,则在前一个.attr('r',radiusScale之前插入此圆圈(d.layer2value))。style('fill',orangeColour); xPos + = currMaxRadius * 0.9; / * *在CIRCLES下添加标签* / group.append('text')。text(d.name).attr('dy',radiusScale(maxValue)+ 40)//添加40px的填充因此标签不仅仅是吼圈子.style('text-anchor','middle'); group.append('line')。attr('y1',radiusScale(d.layer2value))。attr('y2',radiusScale(maxValue)+ 20)//添加20px的填充,使指针线不重叠label .style('stroke',orangeColour); / * *在相对于圆圈的45度的角度添加标签* / //我们正在创建新的组元素,因此我们可以轻松地旋转线条和标签-45deg const rotatingLabelGroup = group.append('g')。style( 'transform','rotate(-45deg)'); rotateLabelGroup.append('line')。attr('x1',radiusScale(d.layer2value))。attr('x2',radiusScale(maxValue)+ 20).style('stroke',orangeColour); rotateLabelGroup.append('text')。text(d.vists).attr('dx',radiusScale(maxValue))。attr('dy', -  5); //这种标签略高于行}< / script>< / body>  


I am working on an application that uses foursquare data.

//this is the series chart that has had some delving into - but there are some bugs still running around here.

So we have a batch of data - Health & Beauty, Restaurants, Cafe, Public Houses. -- there would be a COUNT of them -- and a SUMMATION of checkout information. So I want this chart to be able to show the NUMBER of venues, but also indicate how POPULAR they are.. so for example the number of pubs may be smaller, but the number of checkins higher as they are more popular. So in that instance want to reverse the colors of the circles.

There are some bugs with the current code attempts.

  • the swapping of the circles/circle spacing causes tears in black paths and odd behaviors
  • with the lines I would like to have a black line under the blue circle, but inside the blue circle show a cropped circle path orange line -- so a kind of masking ability.

_latest jsfiddle

phase1

using "V" instead of "L" but couldn't make it work properly for the time being.

phase 2 I think it works more consistently but there are some issues. Also, I am not sure about the data and the scaling of the circles. (I've added extra labels so that it is visible what the value of the circles are)

phase 3

changed the getCircleSize a bit even though I believe a more consistent thing to do would be something like this layerSet.push(parseInt(getPercentage(layerArray[i], meansPerGroup[0])*60, 10));

so here the first step draws the circles by size order first... so in this case by count.. but maybe there is a bug here reversing the color to indicate the checkin count instead - so maybe we need to sort by count,checkin order - that way the first circle to get painted follows correctly.

  // Create Circles
  function setCircles(items) {
    // sort elements in order to draw them by size
    items.sort(function(a, b) {
      return parseFloat(b.value) - parseFloat(a.value);
    });

    var circlelayer = svg.append("g")
      .attr("class", "circlelayer");

    var circle = circlelayer.selectAll("circle")
      .data(items);

    circle.enter().append("circle")
      .attr("class", function(d, i) {
        if (d.l == 0) {
          return "blue";
        }
        return "gold";
      })
      .attr("cy", 60)
      .attr("cx", function(d, i) {
        var distance = calculateDistance(d, items);
        if (d.l == 1) {
          distancesL1.push(distance);
        } else {
          distancesL0.push(distance);
        }
        return distance;
      })
      .attr("r", function(d, i) {
        return Math.sqrt(d.value);
      })
      .attr("filter", function(d) {
        return "url(#drop-shadow)";
      });

    circle.exit().remove();
  }

json structure to look something like this

[{
    "label": "Health and Beauty",
    "count": 30,
    "checkin": 100
}, {
    "label": "Restaurants",
    "count": 23,
    "checkin": 200
}, {
    "label": "Cafes",
    "count": 11,
    "checkin": 900
}, {
    "label": "Public Houses",
    "count": 5,
    "checkin": 1000
}]

解决方案

I'm not sure I understand what is your problem but I've decided to try to create that chart from you screenshot with sample data from your plunker. Here is my result:

My script is making sure that smaller circle is always on top of the rage one so both circles are always visible. Here you can find my code:

<!DOCTYPE html>

<head>
  <meta charset="utf-8">
  <script src="https://d3js.org/d3.v5.min.js"></script>
  <style>
    html {
      font-family: sans-serif;
    }
  </style>
</head>

<body>
  <div id="container">

  </div>

  <script>
    const data = [{
      "name": "Twitter",
      "vists": "15 billion",
      "unit": "per day",
      "layer1value": 15000000,
      "layer2value": 450
    }, {
      "name": "Facebook",
      "vists": "5 billion",
      "unit": "per day",
      "layer1value": 4000000,
      "layer2value": 5000000
    }, {
      "name": "Google",
      "vists": "5 billion",
      "unit": "per day",
      "layer1value": 5000000,
      "layer2value": 25000
    }, {
      "name": "Netflix",
      "vists": "10 billion",
      "unit": "per day",
      "layer1value": 3000000,
      "layer2value": 2200
    }, {
      "name": "Ebay",
      "vists": "8 billion",
      "unit": "per day",
      "layer1value": 2500000,
      "layer2value": 4900000
    }, {
      "name": "Klout",
      "vists": "2 billion",
      "unit": "per day",
      "layer1value": 1000000,
      "layer2value": 45
    }];

    /*
     * Finding max and min layer size
     */
    const values = data.reduce((acumulator, datum) => [...acumulator, datum.layer1value, datum.layer2value], []);
    const maxValue = Math.max(...values);
    const minValue = Math.min(...values);

    /*
     * Creating scale based on the smallest and largest layer1value or layer2value
     */
    const radiusScale = d3.scaleLinear()
      .domain([minValue, maxValue])
      .range([10, 150]); // min and max value of the circle

    const width = 900;
    const height = 500;
    const orangeColour = '#ffb000';
    const blueColour = '#00a1ff';

    // Creating svg element
    const svg = d3.select('#container').append('svg').attr('width', width).attr('height', height);

    let xPos = 0; // position of circle
    /*
     * iterate over each datum and render all associated elements: two circles, and two labels with pointer lines
     */
    for (let i = 0; i < data.length; i++) {
      const d = data[i]; // current data point
      const currMaxRadius = radiusScale(Math.max(d.layer1value, d.layer2value)); // get largest radius within current group of two layers
      xPos += currMaxRadius; // add that radius to xPos

      // create group element containing all view elements for current datum
      const group = svg.append('g')
        .attr('transform', `translate(${xPos}, ${height / 2})`);

      group.append('circle')
        .attr('r', radiusScale(d.layer1value))
        .style('fill', blueColour);
      group.insert('circle', d.layer2value > d.layer1value ? ':first-child' : null) // if layer2value is larger than layer1value then insert this circle before the previous one
        .attr('r', radiusScale(d.layer2value))
        .style('fill', orangeColour);
      xPos += currMaxRadius * 0.9;

      /*
       * ADDING LABEL UNDERNEATH THE CIRCLES
       */
      group.append('text')
        .text(d.name)
        .attr('dy', radiusScale(maxValue) + 40) // add 40px of padding so label is not just bellow the circle
        .style('text-anchor', 'middle');

      group.append('line')
        .attr('y1', radiusScale(d.layer2value))
        .attr('y2', radiusScale(maxValue) + 20) // add 20px of padding so the pointer line is not overlapping with label
        .style('stroke', orangeColour);


      /*
       * ADDING LABEL AT THE ANGLE OF 45deg RELATIVE TO THE CIRCLES
       */
      // we are creating new group element so we can easily rotate both line and label by -45deg
      const rotatedLabelGroup = group.append('g').style('transform', 'rotate(-45deg)');
      rotatedLabelGroup.append('line')
        .attr('x1', radiusScale(d.layer2value))
        .attr('x2', radiusScale(maxValue) + 20)
        .style('stroke', orangeColour);
      rotatedLabelGroup.append('text')
        .text(d.vists)
        .attr('dx', radiusScale(maxValue))
        .attr('dy', -5); // this way label is slightly above the line
    }
  </script>
</body>

这篇关于d3.js检查/计数系列图表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆