I want to draw additional line on x-axis which appear at 0 point in y-axis. chart is created on dynamic values so can not provide fixed position. It requires to read the position of x-axis where point 0 appears then draw line. We can not bisect line as 0 position can be anywhere in line not specifically in middle.
Code for current graph is added below
Current graph:
Expected graph:
var data = [{
"ErrorMessage": null,
"Ymin": -30,
"Ymax": 40,
"Xmin": "2021-04-01 00:00:00",
"Xmax": "2021-04-08 00:00:00",
"Points": [{
Date: "2021-04-01 06:59:06",
Value: 3.43,
Label: "2021-04-01 06:59:06",
data: "BOX"
},
{
Date: "2021-04-02 07:02:05",
Value: -18.43,
Label: "2021-04-02 07:02:05",
data: "BOX"
},
{
Date: "2021-04-03 07:12:06",
Value: 5.43,
Label: "2021-04-03 07:12:06",
data: "BOX"
},
{
Date: "2021-04-04 07:16:07",
Value: 20.43,
Label: "2021-04-04 07:16:07",
data: "BOX"
},
{
Date: "2021-04-03 05:12:20",
Value: 3.43,
Label: "2021-04-03 05:12:20",
data: "Ball"
},
{
Date: "2021-04-05 06:59:06",
Value: 3.43,
Label: "2021-04-05 06:59:06",
data: "Ball"
},
{
Date: "2021-04-06 07:02:05",
Value: 18.43,
Label: "2021-04-06 07:02:05",
data: "Ball"
},
{
Date: "2021-04-07 07:12:06",
Value: 5.43,
Label: "2021-04-07 07:12:06",
data: "Ball"
},
{
Date: "2021-04-07 07:16:07",
Value: -20.43,
Label: "2021-04-07 07:16:07",
data: "BallC"
}
]
}];
function loadChart(data) {
var points = parseData(data.Points);
chartDataLoaded(points[0], "#chart", "#my_dataviz", "#metric-tooltip", "V")
}
function chartDataLoaded(data, chartName, legendName, toolTipName, chartTitle, axisTitle) {
var points = parseData(data.Points);
drawMultilineData(points, data.Xmin, data.Xmax, data.Ymin, data.Ymax, chartName, legendName, toolTipName, chartTitle, axisTitle);
}
function chartDataFailed(data) {
alert("Failed to load chart data");
}
function parseData(points) {
var timeParser = d3.timeParse("%Y-%m-%d %H:%M:%S");
var arr = [];
for (var i in points) {
arr.push({
date: timeParser(points[i].Date), // Date
value: points[i].Value, // Convert string to number
label: points[i].Label,
name: points[i].data
});
}
console.log(arr);
var expensesByName = d3.nest()
.key(function(d) {
return d.name;
})
.entries(arr);
console.log(expensesByName);
return expensesByName;
}
chartDataLoaded(data[0], "#chart", "#my_dataviz", "#metric-tooltip", "test", "V")
function drawMultilineData(data, xmin, xmax, ymin, ymax, chartName, legendName, toolTipName, chartTitle, axisTitle) {
//#region define outline and parameter
var width = 500;
var height = 300;
var margin = 100;
var duration = 250;
var lineOpacity = "0.25";
var lineOpacityHover = "0.85";
var otherLinesOpacityHover = "0.1";
var lineStroke = "2px";
var lineStrokeHover = "3px";
var circleOpacity = '0.85';
var circleOpacityOnLineHover = "0.25"
var circleRadius = 4;
var circleRadiusHover = 6;
//#endregion
//#region get user defined color or random
var color = d3.scaleOrdinal(d3.schemeCategory10);
var colors = {
};
//#endregion
//time parser for min and max
var timeParser = d3.timeParse("%Y-%m-%d %H:%M:%S");
//#region create axis and scale
var xScale = d3.scaleTime().rangeRound([0, width]).domain([timeParser(xmin), timeParser(xmax)]);
var yScale = d3.scaleLinear().rangeRound([height, 0]).domain([ymin, ymax]);
var xAxis = d3.axisBottom(xScale).ticks(8);
var yAxis = d3.axisLeft(yScale);
/* Add SVG */
var svg = d3.select(chartName).append("svg")
.attr("width", (width + margin + 100) + "px") //added to increase x axis to edge
.attr("height", (height + margin + 100) + "px") //added to increase y axis to edge
.append('g')
.attr("transform", `translate(${margin}, ${margin})`);
svg.append("g")
.attr("class", "x axis")
.attr('transform', 'translate(0, ' + height + ')') //to start x-axis point
.call(xAxis)
.selectAll("text")
.attr("transform", "translate(-10,10)rotate(-45)")
.style("text-anchor", "end")
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append('text')
.attr("y", 150)
.attr("x", -30)
.style("font-size", "14px")
.style("font-weight", "bold")
.attr("fill", "#000")
.text(axisTitle)
//#endregion
//#region create lines
/* Add line into SVG */
var line = d3.line()
.x(d => xScale(d.date))
.y(d => yScale(d.value));
let lines = svg.append('g')
.attr('class', 'lines');
lines.selectAll('.line-group')
.data(data).enter()
.append('g')
.attr('class', 'line-group')
.on("mouseover", function(d, i) {
svg.select(".title-text").remove();
svg.append("text")
.attr("class", "title-text")
.style("fill", (colors[d.key] === undefined) ? color(d.key) : colors[d.key])
.text(d.key)
.attr("text-anchor", "middle")
.attr("x", (width - margin) / 2)
.attr("y", 5);
})
.on("mouseout", function(d) {
svg.select(".title-text").remove();
})
.append('path')
.attr('class', 'line')
.attr('d', d => line(d.values))
.style('stroke', (d, i) => (colors[d.key] === undefined) ? color(d.key) : colors[d.key])
.style('opacity', lineOpacity)
.on("mouseover", function(d) {
d3.selectAll('.line')
.style('opacity', otherLinesOpacityHover);
d3.selectAll('.circle')
.style('opacity', circleOpacityOnLineHover);
d3.select(this)
.style('opacity', lineOpacityHover)
.style("stroke-width", lineStrokeHover)
.style("cursor", "pointer");
})
.on("mouseout", function(d) {
d3.selectAll(".line")
.style('opacity', lineOpacity);
d3.selectAll('.circle')
.style('opacity', circleOpacity);
d3.select(this)
.style("stroke-width", lineStroke)
.style("cursor", "none");
});
//#endregion
//#region create circle
/* Add circles in the line */
lines.selectAll("circle-group")
.data(data).enter()
.append("g")
.style("fill", (d, i) => (colors[d.key] === undefined) ? color(d.key) : colors[d.key])
.selectAll("circle")
.data(d => d.values).enter()
.append("g")
.attr("class", "circle")
.on("mouseover", function(d) {
d3.select(this)
.style("cursor", "pointer")
.append("text")
.attr("class", "text")
.attr("x", d => xScale(d.date) + 5)
.attr("y", d => yScale(d.value) - 10);
return tooltip2.style("visibility", "visible");
})
.on("mousemove", function(d) {
return tooltip2.style("left", (d3.mouse(this)[0]) + "px").style("top", (d3.mouse(this)[1]) + "px")
.html("<span>" + d.label + "<br/>" + d.value + "</span>");
})
.on("mouseout", function(d) {
d3.select(this)
.style("cursor", "none")
.transition()
.duration(duration)
.selectAll(".text").remove();
return tooltip2.style("visibility", "hidden");
})
.append("circle")
.attr("cx", d => xScale(d.date))
.attr("cy", d => yScale(d.value))
.attr("r", circleRadius)
.style('opacity', circleOpacity)
.on("mouseover", function(d) {
d3.select(this)
.transition()
.duration(duration)
.attr("r", circleRadiusHover);
})
.on("mouseout", function(d) {
d3.select(this)
.transition()
.duration(duration)
.attr("r", circleRadius);
});
//#endregion
}
svg {
font-family: Sans-Serif, Arial;
}
.line {
stroke-width: 2;
fill: none;
}
.axis path {
stroke: black;
}
.text {
font-size: 12px;
}
.title-text {
font-size: 12px;
}
<html>
<head>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-+YQ4JLhjyBLPDQt//I+STsc9iw4uQqACwlvpslubQzn4u2UU2UFM80nGisd026JF" crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
</head>
<body>
<div class="col-md-12 row">
<div class="col-md-8">
<div id="chart"></div>
</div>
</div>
</body>
<script src="test.js"></script>
</html>
Add the following code:
const yZero = yScale(0);
svg.append('line')
.attr('x1', 0)
.attr('x2', width)
.attr('y1', yZero)
.attr('y2', yZero)
.style('stroke', '#000');
var data = [{
"ErrorMessage": null,
"Ymin": -30,
"Ymax": 40,
"Xmin": "2021-04-01 00:00:00",
"Xmax": "2021-04-08 00:00:00",
"Points": [{
Date: "2021-04-01 06:59:06",
Value: 3.43,
Label: "2021-04-01 06:59:06",
data: "BOX"
},
{
Date: "2021-04-02 07:02:05",
Value: -18.43,
Label: "2021-04-02 07:02:05",
data: "BOX"
},
{
Date: "2021-04-03 07:12:06",
Value: 5.43,
Label: "2021-04-03 07:12:06",
data: "BOX"
},
{
Date: "2021-04-04 07:16:07",
Value: 20.43,
Label: "2021-04-04 07:16:07",
data: "BOX"
},
{
Date: "2021-04-03 05:12:20",
Value: 3.43,
Label: "2021-04-03 05:12:20",
data: "Ball"
},
{
Date: "2021-04-05 06:59:06",
Value: 3.43,
Label: "2021-04-05 06:59:06",
data: "Ball"
},
{
Date: "2021-04-06 07:02:05",
Value: 18.43,
Label: "2021-04-06 07:02:05",
data: "Ball"
},
{
Date: "2021-04-07 07:12:06",
Value: 5.43,
Label: "2021-04-07 07:12:06",
data: "Ball"
},
{
Date: "2021-04-07 07:16:07",
Value: -20.43,
Label: "2021-04-07 07:16:07",
data: "BallC"
}
]
}];
function loadChart(data) {
var points = parseData(data.Points);
chartDataLoaded(points[0], "#chart", "#my_dataviz", "#metric-tooltip", "V")
}
function chartDataLoaded(data, chartName, legendName, toolTipName, chartTitle, axisTitle) {
var points = parseData(data.Points);
drawMultilineData(points, data.Xmin, data.Xmax, data.Ymin, data.Ymax, chartName, legendName, toolTipName, chartTitle, axisTitle);
}
function chartDataFailed(data) {
alert("Failed to load chart data");
}
function parseData(points) {
var timeParser = d3.timeParse("%Y-%m-%d %H:%M:%S");
var arr = [];
for (var i in points) {
arr.push({
date: timeParser(points[i].Date), // Date
value: points[i].Value, // Convert string to number
label: points[i].Label,
name: points[i].data
});
}
console.log(arr);
var expensesByName = d3.nest()
.key(function(d) {
return d.name;
})
.entries(arr);
console.log(expensesByName);
return expensesByName;
}
chartDataLoaded(data[0], "#chart", "#my_dataviz", "#metric-tooltip", "test", "V")
function drawMultilineData(data, xmin, xmax, ymin, ymax, chartName, legendName, toolTipName, chartTitle, axisTitle) {
//#region define outline and parameter
var width = 500;
var height = 300;
var margin = 100;
var duration = 250;
var lineOpacity = "0.25";
var lineOpacityHover = "0.85";
var otherLinesOpacityHover = "0.1";
var lineStroke = "2px";
var lineStrokeHover = "3px";
var circleOpacity = '0.85';
var circleOpacityOnLineHover = "0.25"
var circleRadius = 4;
var circleRadiusHover = 6;
//#endregion
//#region get user defined color or random
var color = d3.scaleOrdinal(d3.schemeCategory10);
var colors = {
};
//#endregion
//time parser for min and max
var timeParser = d3.timeParse("%Y-%m-%d %H:%M:%S");
//#region create axis and scale
var xScale = d3.scaleTime().rangeRound([0, width]).domain([timeParser(xmin), timeParser(xmax)]);
var yScale = d3.scaleLinear().rangeRound([height, 0]).domain([ymin, ymax]);
var xAxis = d3.axisBottom(xScale).ticks(8);
var yAxis = d3.axisLeft(yScale);
/* Add SVG */
var svg = d3.select(chartName).append("svg")
.attr("width", (width + margin + 100) + "px") //added to increase x axis to edge
.attr("height", (height + margin + 100) + "px") //added to increase y axis to edge
.append('g')
.attr("transform", `translate(${margin}, ${margin})`);
svg.append("g")
.attr("class", "x axis")
.attr('transform', 'translate(0, ' + height + ')') //to start x-axis point
.call(xAxis)
.selectAll("text")
.attr("transform", "translate(-10,10)rotate(-45)")
.style("text-anchor", "end")
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append('text')
.attr("y", 150)
.attr("x", -30)
.style("font-size", "14px")
.style("font-weight", "bold")
.attr("fill", "#000")
.text(axisTitle)
//#endregion
//#region create lines
/* Add line into SVG */
var line = d3.line()
.x(d => xScale(d.date))
.y(d => yScale(d.value));
const yZero = yScale(0);
svg.append('line')
.attr('x1', 0)
.attr('x2', width)
.attr('y1', yZero)
.attr('y2', yZero)
.style('stroke', '#000');
let lines = svg.append('g')
.attr('class', 'lines');
lines.selectAll('.line-group')
.data(data).enter()
.append('g')
.attr('class', 'line-group')
.on("mouseover", function(d, i) {
svg.select(".title-text").remove();
svg.append("text")
.attr("class", "title-text")
.style("fill", (colors[d.key] === undefined) ? color(d.key) : colors[d.key])
.text(d.key)
.attr("text-anchor", "middle")
.attr("x", (width - margin) / 2)
.attr("y", 5);
})
.on("mouseout", function(d) {
svg.select(".title-text").remove();
})
.append('path')
.attr('class', 'line')
.attr('d', d => line(d.values))
.style('stroke', (d, i) => (colors[d.key] === undefined) ? color(d.key) : colors[d.key])
.style('opacity', lineOpacity)
.on("mouseover", function(d) {
d3.selectAll('.line')
.style('opacity', otherLinesOpacityHover);
d3.selectAll('.circle')
.style('opacity', circleOpacityOnLineHover);
d3.select(this)
.style('opacity', lineOpacityHover)
.style("stroke-width", lineStrokeHover)
.style("cursor", "pointer");
})
.on("mouseout", function(d) {
d3.selectAll(".line")
.style('opacity', lineOpacity);
d3.selectAll('.circle')
.style('opacity', circleOpacity);
d3.select(this)
.style("stroke-width", lineStroke)
.style("cursor", "none");
});
//#endregion
//#region create circle
/* Add circles in the line */
lines.selectAll("circle-group")
.data(data).enter()
.append("g")
.style("fill", (d, i) => (colors[d.key] === undefined) ? color(d.key) : colors[d.key])
.selectAll("circle")
.data(d => d.values).enter()
.append("g")
.attr("class", "circle")
.on("mouseover", function(d) {
d3.select(this)
.style("cursor", "pointer")
.append("text")
.attr("class", "text")
.attr("x", d => xScale(d.date) + 5)
.attr("y", d => yScale(d.value) - 10);
return tooltip2.style("visibility", "visible");
})
.on("mousemove", function(d) {
return tooltip2.style("left", (d3.mouse(this)[0]) + "px").style("top", (d3.mouse(this)[1]) + "px")
.html("<span>" + d.label + "<br/>" + d.value + "</span>");
})
.on("mouseout", function(d) {
d3.select(this)
.style("cursor", "none")
.transition()
.duration(duration)
.selectAll(".text").remove();
return tooltip2.style("visibility", "hidden");
})
.append("circle")
.attr("cx", d => xScale(d.date))
.attr("cy", d => yScale(d.value))
.attr("r", circleRadius)
.style('opacity', circleOpacity)
.on("mouseover", function(d) {
d3.select(this)
.transition()
.duration(duration)
.attr("r", circleRadiusHover);
})
.on("mouseout", function(d) {
d3.select(this)
.transition()
.duration(duration)
.attr("r", circleRadius);
});
//#endregion
}
svg {
font-family: Sans-Serif, Arial;
}
.line {
stroke-width: 2;
fill: none;
}
.axis path {
stroke: black;
}
.text {
font-size: 12px;
}
.title-text {
font-size: 12px;
}
<html>
<head>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-+YQ4JLhjyBLPDQt//I+STsc9iw4uQqACwlvpslubQzn4u2UU2UFM80nGisd026JF" crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
</head>
<body>
<div class="col-md-12 row">
<div class="col-md-8">
<div id="chart"></div>
</div>
</div>
</body>
<script src="test.js"></script>
</html>