d3js - Как обновить существующий путь SVG в простой линейной диаграмме d3 с новыми данными, проходящими через? - PullRequest
0 голосов
/ 08 ноября 2019

новичок в d3 здесь развлекается, но немного застрял. Я пытаюсь создать простой график, на котором будет построена линия для некоторых необработанных ценовых данных в формате (х - дата, у - значение) и средней линии. У меня есть код, работающий так, что я могу создать строку в svg для набора статических данных.

Теперь я пытаюсь обновить набор данных новым потоком данных, и я хочу, чтобысуществующая линия должна быть расширена, так как я хочу построить серию данных, поэтому там тоже нужны старые данные.

Может кто-нибудь помочь мне найти способ сделать это? Возможно ли это без удаления старой линии и повторного рисования? Т.е. я хочу это расширить. Это может быть достигнуто с помощью ввода выходного шаблона обновления? Многие примеры, которые я видел для этого шаблона, всегда добавляют / удаляют новые фигуры, а не соединяют / расширяют, если это имеет смысл.

Я создал простую линейную диаграмму d3, которая отображает линию для статических данных, ноКогда я использую setInterval, чтобы пропинговать некоторые новые данные, а не расширять линию, он добавляет новые.

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

<script>
        
                var data_set = [
                    { 'Date': '2009-03-23', 'Raw': 25, 'Raw2': 25, 'Asset': 'A' },
                    { 'Date': '2009-03-24', 'Raw': 28, 'Raw2': 25.4, 'Asset': 'A' },
                    { 'Date': '2009-03-25', 'Raw': 26, 'Raw2': 25.37, 'Asset': 'A' },
                    { 'Date': '2009-03-26', 'Raw': 22, 'Raw2': 25.03, 'Asset': 'A' },
                    { 'Date': '2009-03-27', 'Raw': 19, 'Raw2': 24.42, 'Asset': 'A' },
                    { 'Date': '2009-03-28', 'Raw': 23, 'Raw2': 24.28, 'Asset': 'A' }]

                // All of these vars have nothing to do with data yet - it's all setup code
                var margin = { top: 30, right: 50, bottom: 30, left: 50 };
                var svgWidth = 1200;
                var svgHeight = 1000;
                var graphWidth = svgWidth - margin.left - margin.right;
                var graphHeight = svgHeight - margin.top - margin.bottom;
                // var parseDate = d3.timeParse("%d/%m/%Y");
                var parseDate = d3.timeParse("%Y-%m-%d");
                
                
                var x = d3.scaleTime().range([0, graphWidth]);
                var y = d3.scaleLinear().range([graphHeight, 0]);
                var z = d3.scaleOrdinal(d3.schemeCategory10); // for colours
                var xAxis = d3.axisBottom().scale(x).ticks(10);
                var yAxis = d3.axisLeft().scale(y).ticks(10);

                // Need to create the lines manually for each bit of data
                var line1 = d3.line()
                    .x(function (d) { return x(d.Date); })
                    .y(function (d) { return y(d.Raw); });
                var line2 = d3.line()
                    .x(function (d) { return x(d.Date); })
                    .y(function (d) { return y(d.Raw2); });

                // Creates the SVG area within the div on the dom 
                // Just doing this once 
                var svg = d3.select("#graphDiv")
                    .append("svg")
                    .attr("width", svgWidth)
                    .attr("height", svgHeight)
                    .append("g")
                    .attr("transform",
                        "translate(" + margin.left + "," + margin.top + ")")
                     .call(d3.zoom().on("zoom", function () {
                         svg.attr("transform", d3.event.transform)
                     }));


                    // Add the X Axis
                    svg.append("g").attr("class", "x axis")
                       .attr("transform", "translate(0," + graphHeight + ")")
                        .call(xAxis);
                    // Text label for x axis 
                    svg.append("text")
                        .style("text-anchor", "middle")
                        .text("timeseries dates")
                        .attr("transform", "translate(" + (graphWidth / 2) + " ," + (graphHeight + margin.top) + ")");

                    // Add the Y Axis
                    svg.append("g")
                        .attr("class", "y axis")
                        .call(yAxis);
                    // text label for the y axis
                    svg.append("text")
                        .attr("transform", "rotate(-90)")
                        .attr("y", 0 - margin.left)
                        .attr("x", 0 - (graphHeight / 2))
                        .attr("dy", "1em")
                        .style("text-anchor", "middle")
                        .text("price points");

                
                function drawGraph(data_set) {
                    // Pass in the data here 
                    data_set.forEach(function (d) {
                        d.Date = parseDate(d.Date);
                        // + converts the strings to numbers from dataset
                        d.Raw = +d.Raw;
                        d.EWMA = +d.Raw2;
                    });
                    // Domains here represent the complete set of valaues e.g all dates
                    // These are set based on loaded data 
                    x.domain(d3.extent(data_set, function (d) { return d.Date; }));
                    y.domain([d3.min(data_set, function (d) { return Math.min(d.Raw, d.Raw2) }),
                    d3.max(data_set, function (d) { return Math.max(d.Raw, d.Raw2) })]
                    );
                    
                    var lines = svg.selectAll("path").data(data_set)
                     lines.enter().append("path")
                          .attr("class", "dataLine1")
                          .style("stroke", "pink")
                          .style("fill", "none").attr("d", line1(data_set))
                     lines.enter().append("path")
                          .attr("class", "dataLine2")
                          .style("stroke", "#21e0f4").attr("d",line2(data_set));
                    // // Remove any existing ones
                    lines.exit().remove()
                    
                }
                // display initial chart
                window.onload = drawGraph(data_set)
                // Push new data every 5 seconds for a specific date
                var h = setInterval(function () {
                     data_set.push({ 'Date': '2009-03-29', 'Raw': Math.floor(Math.random() * 50), 'Raw2': Math.floor(Math.random() * 25), 'Asset': 'A' }
                    ,{ 'Date': '2009-03-30', 'Raw': Math.floor(Math.random() * 50), 'Raw2': Math.floor(Math.random() * 25), 'Asset': 'A' },
                    { 'Date': '2009-03-31', 'Raw': Math.floor(Math.random() * 50), 'Raw2': Math.floor(Math.random() * 25), 'Asset': 'A' },
                    { 'Date': '2009-04-01', 'Raw': Math.floor(Math.random() * 50), 'Raw2': Math.floor(Math.random() * 25), 'Asset': 'A' },
                    { 'Date': '2009-04-02', 'Raw': Math.floor(Math.random() * 50), 'Raw2': Math.floor(Math.random() * 25), 'Asset': 'A' },
                    { 'Date': '2009-04-03', 'Raw': Math.floor(Math.random() * 50), 'Raw2': Math.floor(Math.random() * 25), 'Asset': 'A' });                    console.log('pushing');
                    drawGraph(data_set);
                    clearInterval(h); //doing this so that it doesnt spam - if i uncomment this, it will keep spamming new lines
                }, 5000);
            </script>

Я ожидаю, что будет две строки, одна для необработанного значения и однадля значения raw2. Через 5 секунд я добавляю новые данные и ожидаю, что обе строки будут расширены

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

1 Ответ

0 голосов
/ 12 ноября 2019

var data_set = [{
    'Date': '2009-03-23',
    'Raw': 25,
    'Raw2': 25,
    'Asset': 'A'
  },
  {
    'Date': '2009-03-24',
    'Raw': 28,
    'Raw2': 25.4,
    'Asset': 'A'
  },
  {
    'Date': '2009-03-25',
    'Raw': 26,
    'Raw2': 25.37,
    'Asset': 'A'
  },
  {
    'Date': '2009-03-26',
    'Raw': 22,
    'Raw2': 25.03,
    'Asset': 'A'
  },
  {
    'Date': '2009-03-27',
    'Raw': 19,
    'Raw2': 24.42,
    'Asset': 'A'
  },
  {
    'Date': '2009-03-28',
    'Raw': 23,
    'Raw2': 24.28,
    'Asset': 'A'
  }
]


var margin = {
  top: 30,
  right: 50,
  bottom: 30,
  left: 50
};
var svgWidth = 600;
var svgHeight = 1000;
var graphWidth = svgWidth - margin.left - margin.right;
var graphHeight = svgHeight - margin.top - margin.bottom;
// var parseDate = d3.timeParse("%d/%m/%Y");
var parseDate = d3.timeParse("%Y-%m-%d");

var x = d3.scaleTime().range([0, graphWidth]);
var y = d3.scaleLinear().range([graphHeight, 0]);
var z = d3.scaleOrdinal(d3.schemeCategory10); // for colours
var xAxis = d3.axisBottom().scale(x).ticks(10);
var yAxis = d3.axisLeft().scale(y).ticks(10);

// Need to create the lines manually for each bit of data
var line = d3.line()
  .x(function(d) {
    return x(d.date);
  })
  .y(function(d) {
    return y(d.y);
  });

// Creates the SVG area within the div on the dom 
// Just doing this once 
var svg = d3.select("#graphDiv")
  .append("svg")
  .attr("width", svgWidth)
  .attr("height", svgHeight)
var g = svg.append("g")
  .attr("transform",
    "translate(" + margin.left + "," + margin.top + ")")
  .call(d3.zoom().on("zoom", function() {
    svg.attr("transform", d3.event.transform)
  }));

// Add the X Axis
g.append("g").attr("class", "x axis")
  .attr("transform", "translate(0," + graphHeight + ")")
  .call(xAxis);
// Text label for x axis 
g.append("text")
  .style("text-anchor", "middle")
  .text("timeseries dates")
  .attr("transform", "translate(" + (graphWidth / 2) + " ," + (graphHeight + margin.top) + ")");

// Add the Y Axis
g.append("g")
  .attr("class", "y axis")
  .call(yAxis);
// text label for the y axis
g.append("text")
  .attr("transform", "rotate(-90)")
  .attr("y", 0 - margin.left)
  .attr("x", 0 - (graphHeight / 2))
  .attr("dy", "1em")
  .style("text-anchor", "middle")
  .text("price points");


function drawGraph(data_set) {

  let pathData = []
  //assume 2 paths
  pathData.push([])
  pathData.push([])

  // Pass in the data here 
  data_set.forEach(function(d) {

    let path0 = {}
    let path1 = {}

    path0.date = parseDate(d.Date)
    path1.date = parseDate(d.Date)

    path0.y = +d.Raw
    path1.y = +d.Raw2

    pathData[0].push(path0)
    pathData[1].push(path1)

  });

  x.domain(d3.extent(data_set, function(d) {
    return parseDate(d.Date);
  }));

  y.domain([
    d3.min(data_set, function(d) {
      return Math.min(d.Raw, d.Raw2)
    }),
    d3.max(data_set, function(d) {
      return Math.max(d.Raw, d.Raw2)
    })
  ]);

  var lines = g.selectAll(".path")
    .data(pathData)

  lines.exit().remove()

  var enter = lines.enter()
    .append("path")
    .attr("class", "path")
    .style("stroke", (d, i) => z(i))

  var merge = enter.merge(lines)
    .attr("d", line)



}
// display initial chart
window.onload = drawGraph(data_set)
// Push new data every 5 seconds for a specific date
var h = setInterval(function() {
  data_set.push({
    'Date': '2009-03-29',
    'Raw': Math.floor(Math.random() * 50),
    'Raw2': Math.floor(Math.random() * 25),
    'Asset': 'A'
  }, {
    'Date': '2009-03-30',
    'Raw': Math.floor(Math.random() * 50),
    'Raw2': Math.floor(Math.random() * 25),
    'Asset': 'A'
  }, {
    'Date': '2009-03-31',
    'Raw': Math.floor(Math.random() * 50),
    'Raw2': Math.floor(Math.random() * 25),
    'Asset': 'A'
  }, {
    'Date': '2009-04-01',
    'Raw': Math.floor(Math.random() * 50),
    'Raw2': Math.floor(Math.random() * 25),
    'Asset': 'A'
  }, {
    'Date': '2009-04-02',
    'Raw': Math.floor(Math.random() * 50),
    'Raw2': Math.floor(Math.random() * 25),
    'Asset': 'A'
  }, {
    'Date': '2009-04-03',
    'Raw': Math.floor(Math.random() * 50),
    'Raw2': Math.floor(Math.random() * 25),
    'Asset': 'A'
  });
  drawGraph(data_set);
  clearInterval(h); //doing this so that it doesnt spam - if i uncomment this, it will keep spamming new lines
}, 5000); >
body {
  margin: 0;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

path {
  fill: none;
  stroke: black
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div id="graphDiv"></div>

Есть несколько проблем с вашим текущим кодом, которые необходимо решить:

  1. Чтобы создать 2 строки на основедля данных было бы лучше, если бы массив содержал 2 элемента, каждый со своим массивом точек данных. Это позволит линейной функции работать так, как задумывалось, и ее можно будет пересматривать в каждой строке, которую вы хотите создать. В приведенном ниже примере я создаю новый набор данных ('pathData') для достижения этой цели.

  2. Шаблон ввода / обновления / слияния необходимо обновить, чтобы строки создавались только один раз,и после этого просто обновите путь ('d' attr), когда поступят новые данные

...