Ненужный пробел в путях линейного графика - PullRequest
0 голосов
/ 18 января 2019

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

Я пытался реализовать этот код: http://jsfiddle.net/SampathPerOxide/bzu63anf/ Я получил двухцветный штрих, который мне нужен, но пути разделяются пробелом, который отключает линейный график. Вот мой полный код: http://jsfiddle.net/SampathPerOxide/ne58otvm/3/

            var data =    [
            {"date":"2018-1","value":40.13,"status":1},
            {"date":"2018-2","value":45.88,"status":1},
            {"date":"2018-3","value":50.89,"status":1},
            {"date":"2018-4","value":55.87,"status":1},
            {"date":"2018-5","value":88.54,"status":1},
            {"date":"2018-6","value":74.41,"status":1},
            {"date":"2018-7","value":98.56,"status":1},
            {"date":"2018-8","value":81.05,"status":1},
            {"date":"2018-9","value":58.13,"status":1},
            {"date":"2018-10","value":95.86,"status":1},
            {"date":"2018-11","value":78.13,"status":1},
            {"date":"2018-12","value":98.86,"status":1},
            {"date":"2019-1","value":105.86,"status":0}, 
            {"date":"2019-2","value":110.86,"status":0}      
            ]; 

            /* Monday 2012 */
            var data1 = data
            var dateformat = "%Y-%m"
            drawTimeSeriesGraph(data1,dateformat);

            /* 
            Tooltip from: http://bl.ocks.org/d3noob/6eb506b129f585ce5c8a
            line graph from here: http://www.d3noob.org/2012/12/starting-with-basic-d3-line-graph.html
            */

            function drawTimeSeriesGraph(data,dateformat) {

            //Set bounds for red dots
            var lbound = 0.045,
            ubound = 0.075;

            // Set the dimensions of the canvas / graph
            var margin = {top: 50, right: 150, bottom: 50, left: 50},
            width = 900- margin.left - margin.right,
            height = 270 - margin.top - margin.bottom;

            // Parse the date / time
            var parseDate = d3.time.format(dateformat).parse,
            formatDate = d3.time.format(dateformat),
            bisectDate = d3.bisector(function(d) { return d.date; }).left;

            // Set the ranges
            var x = d3.time.scale().range([0, width]);
            var y = d3.scale.linear().range([height, 0]);

            // Define the axes
            var xAxis = d3.svg.axis().scale(x)
            .orient("bottom").ticks(10);

            var yAxis = d3.svg.axis().scale(y)
            .orient("left").ticks(10);

            // Define the line
            var valueline = d3.svg.line()
            .x(function(d) { return x(d.date); })
            .y(function(d) { return y(d.value); }).interpolate("linear");
            var full_date = new Date();
            var day = full_date.getDay(); //returns 0 - 6
            var month = full_date.getMonth()+1; //returns 0 - 11
            var year = full_date.getFullYear(); //returns 4 digit year ex: 2000
            var my =  year + "-" + month ;
            //alert(my);
            // Adds the svg canvas
            var svg = d3.select("body")
            .append("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
            .append("g")
            .attr("transform", 
            "translate(" + margin.left + "," + margin.top + ")");

            var lineSvg = svg.append("g"); 

            var focus = svg.append("g") 
            .style("display", "none");

            // Get the data

            data.forEach(function(d) {
            d.date = parseDate(d.date);
            d.value = +d.value;
            });

            // Scale the range of the data
            x.domain(d3.extent(data, function(d) { return d.date; }));
            y.domain([0, d3.max(data, function(d) { return d.value; })]);
            //Use below if instead you want to define the y limits:
            //y.domain([0, 0.11]);

            // Add the valueline path.
            var lineGraph2 = lineSvg.append("path")
            .attr("class", "line")
            .attr("d", valueline(data.filter(function(d) {
            return d.status > 0;
            }))).attr("stroke", "blue").attr("stroke-width", 2)
            .attr("fill", "none");

            var lineGraph1 = lineSvg.append("path")
            .attr("class", "line")
            .attr("d", valueline(data.filter(function(d) {
            return d.status <= 0;
            }))).attr("stroke", "red").attr("stroke-width", 2)
            .attr("fill", "none");

            // Add the X Axis
            svg.append("g")
            .attr("class", "x axis")
            .attr("transform", "translate(0," + height + ")")
            .call(xAxis);

            // Add the Y Axis
            svg.append("g")
            .attr("class", "y axis")
            .call(yAxis);

            // append the x line
            focus.append("line")
            .attr("class", "x")
            .style("stroke", "blue")
            .style("stroke-dasharray", "3,3")
            .style("opacity", 0.5)
            .attr("y1", 0)
            .attr("y2", height);

            // append the y line
            focus.append("line")
            .attr("class", "y")
            .style("stroke", "blue")
            .style("stroke-dasharray", "3,3")
            .style("opacity", 0.5)
            .attr("x1", width)
            .attr("x2", width);

            // append the circle at the intersection
            focus.append("circle")
            .attr("class", "y")
            .style("fill", "none")
            .style("stroke", "blue")
            .attr("r", 4);

            // place the value at the intersection
            focus.append("text")
            .attr("class", "y1")
            .style("stroke", "white")
            .style("stroke-width", "3.5px")
            .style("opacity", 0.8)
            .attr("dx", 8)
            .attr("dy", "-.3em");
            focus.append("text")
            .attr("class", "y2")
            .attr("dx", 8)
            .attr("dy", "-.3em");

            // place the date at the intersection
            focus.append("text")
            .attr("class", "y3")
            .style("stroke", "white")
            .style("stroke-width", "3.5px")
            .style("opacity", 0.8)
            .attr("dx", 8)
            .attr("dy", "1em");
            focus.append("text")
            .attr("class", "y4")
            .attr("dx", 8)
            .attr("dy", "1em");

            // append the rectangle to capture mouse
            svg.append("rect")
            .attr("width", width)
            .attr("height", height)
            .style("fill", "none")
            .style("pointer-events", "all")
            .on("mouseover", function() { focus.style("display", null); })
            .on("mouseout", function() { focus.style("display", "none"); })
            .on("mousemove", mousemove);

            function mousemove() {
            var x0 = x.invert(d3.mouse(this)[0]),
            i = bisectDate(data, x0, 1),
            d0 = data[i - 1],
            d1 = data[i],
            d = x0 - d0.date > d1.date - x0 ? d1 : d0;

            focus.select("circle.y")
            .attr("transform",
            "translate(" + x(d.date) + "," +
            y(d.value) + ")");

            focus.select("text.y1")
            .attr("transform",
            "translate(" + x(d.date) + "," +
            y(d.value) + ")")
            .text(d.value.toFixed(2));

            focus.select("text.y2")
            .attr("transform",
            "translate(" + x(d.date) + "," +
            y(d.value) + ")")
            .text(d.value.toFixed(2));

            focus.select("text.y3")
            .attr("transform",
            "translate(" + x(d.date) + "," +
            y(d.value) + ")")
            .text(formatDate(d.date));

            focus.select("text.y4")
            .attr("transform",
            "translate(" + x(d.date) + "," +
            y(d.value) + ")")
            .text(formatDate(d.date));

            focus.select(".x")
            .attr("transform",
            "translate(" + x(d.date) + "," +
            y(d.value) + ")")
            .attr("y2", height - y(d.value));

            focus.select(".y")
            .attr("transform",
            "translate(" + width * -1 + "," +
            y(d.value) + ")")
            .attr("x2", width + width);
            };

            svg.append("text")
            .attr("x", (width / 2))             
            .attr("y", 0 - (margin.top / 2))
            .attr("text-anchor", "middle")  
            .style("font-size", "16px") 
            .style("text-decoration", "underline")  

            };

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

Ответы [ 2 ]

0 голосов
/ 18 января 2019

Жесткое кодирование позиции slice не очень гибкое.

Определить индекс первой точки со статусом = 0, а затем разделить данные на основе этого индекса.

var firstStatus0 = data.findIndex( e => e.status === 0);
firstStatus0 = firstStatus0 === -1 ? data.length : firstStatus0;

// Add the valueline path.
var lineGraph2 = lineSvg.append("path")
  .attr("class", "line")
  .attr("d", valueline(data.slice(0, firstStatus0)))
  .attr("stroke", "blue").attr("stroke-width", 2)
  .attr("fill", "none");

var lineGraph1 = lineSvg.append("path")
  .attr("class", "line")
  .attr("d", valueline(data.slice(Math.max(0, firstStatus0 - 1))))
  .attr("stroke", "red").attr("stroke-width", 2)
  .attr("fill", "none");
0 голосов
/ 18 января 2019

Код делает именно то, что вы сказали ему сделать: есть только 2 объекта данных с status:0. Таким образом, при фильтрации данных ...

.attr("d", valueline(data.filter(function(d) {
    return d.status <= 0;
})))

... есть только 2 точки для генерации синей линии, которая создает одну линию, идущую от первой ко второй.

Вместо этого просто получите последние три объекта:

.attr("d", valueline(data.slice(-3)))

Таким образом, у вас будет три точки и, следовательно, две линии.

Вот ваш код с этим изменением:

        var data = [{
            "date": "2018-1",
            "value": 40.13,
            "status": 1
          },
          {
            "date": "2018-2",
            "value": 45.88,
            "status": 1
          },
          {
            "date": "2018-3",
            "value": 50.89,
            "status": 1
          },
          {
            "date": "2018-4",
            "value": 55.87,
            "status": 1
          },
          {
            "date": "2018-5",
            "value": 88.54,
            "status": 1
          },
          {
            "date": "2018-6",
            "value": 74.41,
            "status": 1
          },
          {
            "date": "2018-7",
            "value": 98.56,
            "status": 1
          },
          {
            "date": "2018-8",
            "value": 81.05,
            "status": 1
          },
          {
            "date": "2018-9",
            "value": 58.13,
            "status": 1
          },
          {
            "date": "2018-10",
            "value": 95.86,
            "status": 1
          },
          {
            "date": "2018-11",
            "value": 78.13,
            "status": 1
          },
          {
            "date": "2018-12",
            "value": 98.86,
            "status": 1
          },
          {
            "date": "2019-1",
            "value": 105.86,
            "status": 0
          },
          {
            "date": "2019-2",
            "value": 110.86,
            "status": 0
          }
        ];

        /* Monday 2012 */
        var data1 = data
        var dateformat = "%Y-%m"
        drawTimeSeriesGraph(data1, dateformat);

        /* 
        Tooltip from: http://bl.ocks.org/d3noob/6eb506b129f585ce5c8a
        line graph from here: http://www.d3noob.org/2012/12/starting-with-basic-d3-line-graph.html
        */

        function drawTimeSeriesGraph(data, dateformat) {

          //Set bounds for red dots
          var lbound = 0.045,
            ubound = 0.075;

          // Set the dimensions of the canvas / graph
          var margin = {
              top: 50,
              right: 150,
              bottom: 50,
              left: 50
            },
            width = 900 - margin.left - margin.right,
            height = 270 - margin.top - margin.bottom;

          // Parse the date / time
          var parseDate = d3.time.format(dateformat).parse,
            formatDate = d3.time.format(dateformat),
            bisectDate = d3.bisector(function(d) {
              return d.date;
            }).left;

          // Set the ranges
          var x = d3.time.scale().range([0, width]);
          var y = d3.scale.linear().range([height, 0]);

          // Define the axes
          var xAxis = d3.svg.axis().scale(x)
            .orient("bottom").ticks(10);

          var yAxis = d3.svg.axis().scale(y)
            .orient("left").ticks(10);

          // Define the line
          var valueline = d3.svg.line()
            .x(function(d) {
              return x(d.date);
            })
            .y(function(d) {
              return y(d.value);
            }).interpolate("linear");
          var full_date = new Date();
          var day = full_date.getDay(); //returns 0 - 6
          var month = full_date.getMonth() + 1; //returns 0 - 11
          var year = full_date.getFullYear(); //returns 4 digit year ex: 2000
          var my = year + "-" + month;
          //alert(my);
          // Adds the svg canvas
          var svg = d3.select("body")
            .append("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
            .append("g")
            .attr("transform",
              "translate(" + margin.left + "," + margin.top + ")");

          var lineSvg = svg.append("g");

          var focus = svg.append("g")
            .style("display", "none");

          // Get the data

          data.forEach(function(d) {
            d.date = parseDate(d.date);
            d.value = +d.value;
          });

          // Scale the range of the data
          x.domain(d3.extent(data, function(d) {
            return d.date;
          }));
          y.domain([0, d3.max(data, function(d) {
            return d.value;
          })]);
          //Use below if instead you want to define the y limits:
          //y.domain([0, 0.11]);

          // Add the valueline path.
          var lineGraph2 = lineSvg.append("path")
            .attr("class", "line")
            .attr("d", valueline(data.filter(function(d) {
              return d.status > 0;
            }))).attr("stroke", "blue").attr("stroke-width", 2)
            .attr("fill", "none");

          var lineGraph1 = lineSvg.append("path")
            .attr("class", "line")
            .attr("d", valueline(data.slice(-3))).attr("stroke", "red").attr("stroke-width", 2)
            .attr("fill", "none");

          // Add the X Axis
          svg.append("g")
            .attr("class", "x axis")
            .attr("transform", "translate(0," + height + ")")
            .call(xAxis);

          // Add the Y Axis
          svg.append("g")
            .attr("class", "y axis")
            .call(yAxis);

          // append the x line
          focus.append("line")
            .attr("class", "x")
            .style("stroke", "blue")
            .style("stroke-dasharray", "3,3")
            .style("opacity", 0.5)
            .attr("y1", 0)
            .attr("y2", height);

          // append the y line
          focus.append("line")
            .attr("class", "y")
            .style("stroke", "blue")
            .style("stroke-dasharray", "3,3")
            .style("opacity", 0.5)
            .attr("x1", width)
            .attr("x2", width);

          // append the circle at the intersection
          focus.append("circle")
            .attr("class", "y")
            .style("fill", "none")
            .style("stroke", "blue")
            .attr("r", 4);

          // place the value at the intersection
          focus.append("text")
            .attr("class", "y1")
            .style("stroke", "white")
            .style("stroke-width", "3.5px")
            .style("opacity", 0.8)
            .attr("dx", 8)
            .attr("dy", "-.3em");
          focus.append("text")
            .attr("class", "y2")
            .attr("dx", 8)
            .attr("dy", "-.3em");

          // place the date at the intersection
          focus.append("text")
            .attr("class", "y3")
            .style("stroke", "white")
            .style("stroke-width", "3.5px")
            .style("opacity", 0.8)
            .attr("dx", 8)
            .attr("dy", "1em");
          focus.append("text")
            .attr("class", "y4")
            .attr("dx", 8)
            .attr("dy", "1em");

          // append the rectangle to capture mouse
          svg.append("rect")
            .attr("width", width)
            .attr("height", height)
            .style("fill", "none")
            .style("pointer-events", "all")
            .on("mouseover", function() {
              focus.style("display", null);
            })
            .on("mouseout", function() {
              focus.style("display", "none");
            })
            .on("mousemove", mousemove);

          function mousemove() {
            var x0 = x.invert(d3.mouse(this)[0]),
              i = bisectDate(data, x0, 1),
              d0 = data[i - 1],
              d1 = data[i],
              d = x0 - d0.date > d1.date - x0 ? d1 : d0;

            focus.select("circle.y")
              .attr("transform",
                "translate(" + x(d.date) + "," +
                y(d.value) + ")");

            focus.select("text.y1")
              .attr("transform",
                "translate(" + x(d.date) + "," +
                y(d.value) + ")")
              .text(d.value.toFixed(2));

            focus.select("text.y2")
              .attr("transform",
                "translate(" + x(d.date) + "," +
                y(d.value) + ")")
              .text(d.value.toFixed(2));

            focus.select("text.y3")
              .attr("transform",
                "translate(" + x(d.date) + "," +
                y(d.value) + ")")
              .text(formatDate(d.date));

            focus.select("text.y4")
              .attr("transform",
                "translate(" + x(d.date) + "," +
                y(d.value) + ")")
              .text(formatDate(d.date));

            focus.select(".x")
              .attr("transform",
                "translate(" + x(d.date) + "," +
                y(d.value) + ")")
              .attr("y2", height - y(d.value));

            focus.select(".y")
              .attr("transform",
                "translate(" + width * -1 + "," +
                y(d.value) + ")")
              .attr("x2", width + width);
          };

          svg.append("text")
            .attr("x", (width / 2))
            .attr("y", 0 - (margin.top / 2))
            .attr("text-anchor", "middle")
            .style("font-size", "16px")
            .style("text-decoration", "underline")

        };
.axis text {
  font-family: sans-serif;
  font-size: 9px;
}

.axis path {
  fill: none;
  stroke: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...