score:1

Accepted answer

In your data, nodes2.data is not sorted properly. d3 will not sort your data for you, it'll plot it as it's given.

Here's a running sample with the data properly sorted:

<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>

<body>
  <svg id="visualisation" width="900" height="900"></svg>
  <script>
    var data = {
      "nodes": {
        "data": [{
          "name": "sum_ordered",
          "items": [{
            "from": "2017-03-01",
            "sum_ordered": "3",
            "price": false,
            "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u0417\\u0430\\u043a\\u0430\\u0437\\u0430\\u043d\\u043e (\\u0448\\u0442)"
          }, {
            "from": "2017-03-06",
            "sum_ordered": "5",
            "price": false,
            "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u0417\\u0430\\u043a\\u0430\\u0437\\u0430\\u043d\\u043e (\\u0448\\u0442)"
          }, {
            "from": "2017-03-10",
            "sum_ordered": "116",
            "price": false,
            "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u0417\\u0430\\u043a\\u0430\\u0437\\u0430\\u043d\\u043e (\\u0448\\u0442)"
          }, {
            "from": "2017-03-20",
            "sum_ordered": "51",
            "price": false,
            "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u0417\\u0430\\u043a\\u0430\\u0437\\u0430\\u043d\\u043e (\\u0448\\u0442)"
          }],
          "price": false
        }, {
          "name": "sum_income",
          "items": [{
            "from": "2017-03-01",
            "sum_income": "0",
            "price": false,
            "label": "\\u041f\\u043e\\u0441\\u0442\\u0443\\u043f\\u043b\\u0435\\u043d\\u0438\\u044f (\\u0448\\u0442)"
          }, {
            "from": "2017-03-06",
            "sum_income": "0",
            "price": false,
            "label": "\\u041f\\u043e\\u0441\\u0442\\u0443\\u043f\\u043b\\u0435\\u043d\\u0438\\u044f (\\u0448\\u0442)"
          }, {
            "from": "2017-03-10",
            "sum_income": "0",
            "price": false,
            "label": "\\u041f\\u043e\\u0441\\u0442\\u0443\\u043f\\u043b\\u0435\\u043d\\u0438\\u044f (\\u0448\\u0442)"
          }, {
            "from": "2017-03-20",
            "sum_income": "0",
            "price": false,
            "label": "\\u041f\\u043e\\u0441\\u0442\\u0443\\u043f\\u043b\\u0435\\u043d\\u0438\\u044f (\\u0448\\u0442)"
          }],
          "price": false
        }, {
          "name": "sum_sales_by_payment",
          "items": [{
            "from": "2017-03-01",
            "sum_sales_by_payment": "7",
            "price": false,
            "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u041f\\u0440\\u043e\\u0434\\u0430\\u0436\\u0438 \\u043f\\u043e \\u043e\\u043f\\u043b\\u0430\\u0442\\u0430\\u043c (\\u0448\\u0442)"
          }, {
            "from": "2017-03-06",
            "sum_sales_by_payment": "5",
            "price": false,
            "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u041f\\u0440\\u043e\\u0434\\u0430\\u0436\\u0438 \\u043f\\u043e \\u043e\\u043f\\u043b\\u0430\\u0442\\u0430\\u043c (\\u0448\\u0442)"
          }, {
            "from": "2017-03-10",
            "sum_sales_by_payment": "53",
            "price": false,
            "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u041f\\u0440\\u043e\\u0434\\u0430\\u0436\\u0438 \\u043f\\u043e \\u043e\\u043f\\u043b\\u0430\\u0442\\u0430\\u043c (\\u0448\\u0442)"
          }, {
            "from": "2017-03-20",
            "sum_sales_by_payment": "18",
            "price": false,
            "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u041f\\u0440\\u043e\\u0434\\u0430\\u0436\\u0438 \\u043f\\u043e \\u043e\\u043f\\u043b\\u0430\\u0442\\u0430\\u043c (\\u0448\\u0442)"
          }],
          "price": false
        }],
        "xAxis": [
          "2017-03-20",
          "2017-03-20",
          "2017-03-20",
          "2017-03-20",
          "2017-03-10",
          "2017-03-10",
          "2017-03-10",
          "2017-03-10",
          "2017-03-06",
          "2017-03-06",
          "2017-03-06",
          "2017-03-06",
          "2017-03-01",
          "2017-03-01",
          "2017-03-01",
          "2017-03-01"
        ],
        "yAxis": [
          "116",
          "53",
          "51",
          "18",
          "7",
          "5",
          "5",
          "3",
          "0",
          "0",
          "0",
          "0"
        ],
        "zAxis": [],
        "x2Domain": {
          "from": "2017-03-01",
          "to": "2017-03-20"
        },
        "colors": [
          "#19c2ff",
          "#ff9019",
          "#6fc522",
          "#e95fcd",
          "#a02424",
          "#6a56c1",
          "#4fc494",
          "#ffd324",
          "#ff78be"
        ],
        "options": [{
          "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u0417\\u0430\\u043a\\u0430\\u0437\\u0430\\u043d\\u043e (\\u0448\\u0442)",
          "color": "#19c2ff",
          "id": "sum_ordered"
        }, {
          "label": "\\u041f\\u043e\\u0441\\u0442\\u0443\\u043f\\u043b\\u0435\\u043d\\u0438\\u044f (\\u0448\\u0442)",
          "color": "#ff9019",
          "id": "sum_income"
        }, {
          "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u041f\\u0440\\u043e\\u0434\\u0430\\u0436\\u0438 \\u043f\\u043e \\u043e\\u043f\\u043b\\u0430\\u0442\\u0430\\u043c (\\u0448\\u0442)",
          "color": "#6fc522",
          "id": "sum_sales_by_payment"
        }]
      },
      "nodes2": {
        "data": [{
          "name": "sum_ordered",
          "items": [{
            "from": "2017-01-30",
            "sum_ordered": "2",
            "price": false,
            "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u0417\\u0430\\u043a\\u0430\\u0437\\u0430\\u043d\\u043e (\\u0448\\u0442)"
          }, {
            "from": "2017-03-01",
            "sum_ordered": "19",
            "price": false,
            "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u0417\\u0430\\u043a\\u0430\\u0437\\u0430\\u043d\\u043e (\\u0448\\u0442)"
          }, {
            "from": "2017-03-09",
            "sum_ordered": "1",
            "price": false,
            "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u0417\\u0430\\u043a\\u0430\\u0437\\u0430\\u043d\\u043e (\\u0448\\u0442)"
          }, {
            "from": "2017-03-10",
            "sum_ordered": "15",
            "price": false,
            "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u0417\\u0430\\u043a\\u0430\\u0437\\u0430\\u043d\\u043e (\\u0448\\u0442)"
          }],
          "price": false
        }, {
          "name": "sum_income",
          "items": [{
            "from": "2017-01-30",
            "sum_income": "0",
            "price": false,
            "label": "\\u041f\\u043e\\u0441\\u0442\\u0443\\u043f\\u043b\\u0435\\u043d\\u0438\\u044f (\\u0448\\u0442)"
          }, {
            "from": "2017-03-01",
            "sum_income": "0",
            "price": false,
            "label": "\\u041f\\u043e\\u0441\\u0442\\u0443\\u043f\\u043b\\u0435\\u043d\\u0438\\u044f (\\u0448\\u0442)"
          }, {
            "from": "2017-03-09",
            "sum_income": "0",
            "price": false,
            "label": "\\u041f\\u043e\\u0441\\u0442\\u0443\\u043f\\u043b\\u0435\\u043d\\u0438\\u044f (\\u0448\\u0442)"
          }, {
            "from": "2017-03-10",
            "sum_income": "0",
            "price": false,
            "label": "\\u041f\\u043e\\u0441\\u0442\\u0443\\u043f\\u043b\\u0435\\u043d\\u0438\\u044f (\\u0448\\u0442)"
          }],
          "price": false
        }, {
          "name": "sum_sales_by_payment",
          "items": [{
            "from": "2017-01-30",
            "sum_sales_by_payment": "1",
            "price": false,
            "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u041f\\u0440\\u043e\\u0434\\u0430\\u0436\\u0438 \\u043f\\u043e \\u043e\\u043f\\u043b\\u0430\\u0442\\u0430\\u043c (\\u0448\\u0442)"
          }, {
            "from": "2017-03-01",
            "sum_sales_by_payment": "5",
            "price": false,
            "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u041f\\u0440\\u043e\\u0434\\u0430\\u0436\\u0438 \\u043f\\u043e \\u043e\\u043f\\u043b\\u0430\\u0442\\u0430\\u043c (\\u0448\\u0442)"
          }, {
            "from": "2017-03-09",
            "sum_sales_by_payment": "0",
            "price": false,
            "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u041f\\u0440\\u043e\\u0434\\u0430\\u0436\\u0438 \\u043f\\u043e \\u043e\\u043f\\u043b\\u0430\\u0442\\u0430\\u043c (\\u0448\\u0442)"
          }, {
            "from": "2017-03-10",
            "sum_sales_by_payment": "11",
            "price": false,
            "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u041f\\u0440\\u043e\\u0434\\u0430\\u0436\\u0438 \\u043f\\u043e \\u043e\\u043f\\u043b\\u0430\\u0442\\u0430\\u043c (\\u0448\\u0442)"
          }],
          "price": false
        }],
        "xAxis": [
          "2017-03-10",
          "2017-03-10",
          "2017-03-10",
          "2017-03-10",
          "2017-03-09",
          "2017-03-09",
          "2017-03-09",
          "2017-03-09",
          "2017-03-01",
          "2017-03-01",
          "2017-03-01",
          "2017-03-01",
          "2017-01-30",
          "2017-01-30",
          "2017-01-30",
          "2017-01-30"
        ],
        "yAxis": [
          "19",
          "15",
          "11",
          "5",
          "2",
          "1",
          "1",
          "0",
          "0",
          "0",
          "0",
          "0"
        ],
        "zAxis": [],
        "x2Domain": {
          "from": "2017-01-30",
          "to": "2017-03-10"
        },
        "colors": [
          "#19c2ff",
          "#ff9019",
          "#6fc522",
          "#e95fcd",
          "#a02424",
          "#6a56c1",
          "#4fc494",
          "#ffd324",
          "#ff78be"
        ],
        "options": [{
          "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u0417\\u0430\\u043a\\u0430\\u0437\\u0430\\u043d\\u043e (\\u0448\\u0442)",
          "color": "#19c2ff",
          "id": "sum_ordered"
        }, {
          "label": "\\u041f\\u043e\\u0441\\u0442\\u0443\\u043f\\u043b\\u0435\\u043d\\u0438\\u044f (\\u0448\\u0442)",
          "color": "#ff9019",
          "id": "sum_income"
        }, {
          "label": "\\u0412\\u0441\\u0435\\u0433\\u043e \\u041f\\u0440\\u043e\\u0434\\u0430\\u0436\\u0438 \\u043f\\u043e \\u043e\\u043f\\u043b\\u0430\\u0442\\u0430\\u043c (\\u0448\\u0442)",
          "color": "#6fc522",
          "id": "sum_sales_by_payment"
        }]
      }
    }

    chart(data, ['red', 'green', 'blue', 'orange'])


    function chart(dataArray, colors) {

      var data = dataArray.nodes;
      // console.log(dataArray.nodes2)

      //$('#visualisation').empty();
      //$('.no-data').hide();

      var MARGINS = {
          top: 20,
          right: 20,
          bottom: 30,
          left: 30
        },
        WIDTH = 600,
        HEIGHT = 600;


      var canvas = d3.select("#visualisation");

      var parseTime = d3.timeParse("%Y-%m-%d");

      var yDomain = [d3.min(data.yAxis, function(d) {
        return Math.min(d)
      }), d3.max(data.yAxis, function(d) {
        return Math.max(d);
      })];
      // var yDomain = [d3.min(dataArray.nodes2.yAxis, function(d) {return Math.min(d)}), d3.max(dataArray.nodes2.yAxis, function(d) {return Math.max(d);})];
      var zDomain = [d3.min(data.zAxis, function(d) {
        return Math.min(d)
      }), d3.max(data.zAxis, function(d) {
        return Math.max(d);
      })];



      xScale = d3.scaleTime().range([0, WIDTH]);
      x2Scale = d3.scaleTime().range([0, WIDTH]);
      yScale = d3.scaleLinear().range([HEIGHT, 0]);
      zScale = d3.scaleLinear().range([HEIGHT, 0]);


      for (var i = 0; i < data.xAxis.length; i++) {
        data.xAxis[i] = parseTime(data.xAxis[i]);
      }
      data.xAxis.sort();

      // console.log(data.xAxis)

      xScale.domain(d3.extent(data.xAxis, function(d) {
        return d;
      }));



      yScale.domain(yDomain);
      zScale.domain(zDomain);

      xAxis = d3.axisBottom()
        .scale(xScale)
        .ticks(10)
        .tickSizeInner(-HEIGHT)
        .tickPadding(10)
        .tickSizeOuter(0);



      yAxis = d3.axisLeft()
        .scale(yScale)
        .tickSizeInner(-WIDTH)
        .tickPadding(10)
        .tickSizeOuter(0)
        .ticks(15);

      zAxis = d3.axisLeft()
        .scale(zScale)
        .ticks(10)

      vis = canvas.append("svg:g")
        .attr("transform", "translate(25,25)")


      vis.append("svg:g")
        .call(zAxis)
        .attr("transform",
          "translate(" + (WIDTH + 30) + ",0)")
        .attr('y', 120 - MARGINS.left);

      vis.append("svg:g")
        .attr('class', 'line-transparent')
        .attr("width", WIDTH + MARGINS.left + MARGINS.right)
        .attr("height", HEIGHT + MARGINS.top + MARGINS.bottom)
        .attr("transform", "translate(25," + HEIGHT + ")")
        .call(xAxis);



      vis.append("svg:g")
        .call(yAxis)
        .attr('class', 'line-transparent')
        .attr("transform",
          "translate(" + MARGINS.left + ",0)")
        .attr('y', 20 - MARGINS.left);


      var zLineGen = d3.line()
        .x(function(d) {
          return xScale(parseTime(d.from)) + 30;
        })
        .y(function(d, name) {
          var names = Object.keys(d);
          return zScale(d[names[1]]);
        });

      if (dataArray.nodes2) {


        for (var i = 0; i < dataArray.nodes2.xAxis.length; i++) {
          dataArray.nodes2.xAxis[i] = parseTime(dataArray.nodes2.xAxis[i]);
        }
        dataArray.nodes2.xAxis.sort();

        // console.log(dataArray.nodes2.xAxis)

        x2Scale.domain(d3.extent(dataArray.nodes2.xAxis, function(d) {
          return d;
        }));
        x2Axis = d3.axisTop()
          .scale(x2Scale)
        vis.append("svg:g")
          .attr('class', 'line-transparent')
          .attr("width", WIDTH + MARGINS.left + MARGINS.right)
          .attr("height", HEIGHT + MARGINS.top + MARGINS.bottom)
          .attr("transform", "translate(25,0)")
          // .attr('y', 0)
          .call(x2Axis);

        makeLines(vis, dataArray.nodes2, 2)
          //makeLabels(dataArray.nodes2.options, 1, dataArray.nodes2.colors)
          //TODO separate it to function
        var line2 = vis.selectAll('.scale_2')
          .data(dataArray.nodes2.data);

        line2.each(function(d) {
            d.items.forEach(function(r) {
              var newNode = vis.append('g');
              newNode
                .attr('class', 'chart-dot')
                .append('text')
                .attr('x', function(el) {
                  var names = Object.keys(r);
                  var text = r.label + ': ' + r[names[1]];
                  var tWidth = getTextWidth(text, 10, 'Arial');
                  if (x2Scale(parseTime(r.from)) > WIDTH / 2) {
                    return x2Scale(parseTime(r.from)) - (tWidth + 10)
                  } else {
                    return x2Scale(parseTime(r.from)) + 30
                  }
                })
                .attr('y', function() {
                  var names = Object.keys(r);
                  if (r.price == true) {
                    return zScale(r[names[1]]);
                  } else {
                    return yScale(r[names[1]]);
                  }
                })
                .attr('font-size', '10')
                .attr('class', 'chart-dot-text')
                .text(function() {
                  var names = Object.keys(r);
                  //return r.from + ' - ' + r.label + ': ' + r[names[1]];
                });

              newNode.append('circle')
                .attr('cx', function() {
                  return x2Scale(parseTime(r.from)) + 30
                })
                .attr('cy', function() {
                  var names = Object.keys(r);
                  if (r.price == true) {
                    return zScale(r[names[1]]);
                  } else {
                    return yScale(r[names[1]]);
                  }
                })
                .attr('r', 5)
            })
          })
          //
      }

      makeLines(vis, data, 1)

      var line = vis.selectAll('.scale_1')
        .data(data.data);

      line.each(function(d) {
        d.items.forEach(function(r) {
          var newNode = vis.append('g');
          newNode
            .attr('class', 'chart-dot')
            .append('text')
            .attr('x', function(el) {
              var names = Object.keys(r);
              var text = r.label + ': ' + r[names[1]];
              var tWidth = getTextWidth(text, 10, 'Arial');
              if (xScale(parseTime(r.from)) > WIDTH / 2) {
                return xScale(parseTime(r.from)) - (tWidth + 10)
              } else {
                return xScale(parseTime(r.from)) + 30
              }
            })
            .attr('y', function() {
              var names = Object.keys(r);
              if (r.price == true) {
                return zScale(r[names[1]]);
              } else {
                return yScale(r[names[1]]);
              }
            })
            .attr('font-size', '10')
            .attr('class', 'chart-dot-text')
            .text(function() {
              var names = Object.keys(r);
              //return r.label + ': ' + r[names[1]];
            });

          newNode.append('circle')
            .attr('cx', function() {
              return xScale(parseTime(r.from)) + 30
            })
            .attr('cy', function() {
              var names = Object.keys(r);
              if (r.price == true) {
                return zScale(r[names[1]]);
              } else {
                return yScale(r[names[1]]);
              }
            })
            .attr('r', 5)
        })
      })

      function makeLines(vis, data, scale) {

        var i = 0;

        data.data.forEach(function(row) {

          if (scale == 1) {
            var stroke = colors[i];
          } else {
            var stroke = colors[(colors.length - 1) - i]
          }

          vis.append('svg:path')
            .attr('d', function(d) {
              if (row.price == true) {
                console.log(row.items)
                return zLineGen(row.items)
              } else {
                console.log(row.items)
                return lineGen(row.items, scale);
              }
            })
            .attr('stroke', stroke)
            .attr('stroke-width', 2)
            .attr('id', function() {
              if (scale == 1) {
                return row.name
              } else {
                return row.name + '_comparison'
              }
            })
            .attr('fill', 'none')
            .attr('class', 'chart-line scale_' + scale)
            .attr('data-scale', scale)
            .on('mousemove', function(d) {
              var x = xScale.invert(d3.mouse(this)[0]);
              var y = yScale.invert(d3.mouse(this)[1]);
            });
          i++;
        });
      }

      function lineGen(s, scale) {
        // console.log(scale)

        if (scale == 1) {
          var newlineGen = d3.line()
            .x(function(d) {
              return xScale(parseTime(d.from)) + 30;
            })
            .y(function(d, name) {
              var names = Object.keys(d);
              return yScale(d[names[1]]);
            });
        } else {
          var newlineGen = d3.line()
            .x(function(d, x) {
              //console.log(d)
              return x2Scale(parseTime(d.from)) + 30;
            })
            .y(function(d, name) {
              var names = Object.keys(d);
              return yScale(d[names[1]]);
            });
        }
        return newlineGen(s)
      }


      function getTextWidth(text, fontSize, fontFace) {
        var a = document.createElement('canvas');
        var b = a.getContext('2d');
        b.font = fontSize + 'px ' + fontFace;
        return b.measureText(text).width;
      }


      function mousemove() {
        var x0 = xScale.invert(d3.mouse(this)[0]);
        var y0 = yScale.invert(d3.mouse(this)[1]);
      };
    }
  </script>
</body>

</html>


Related Query

More Query from same tag