Reputation: 1017
I adapted this example for my data: D3js Graph with X and Y crosshairs, and a threshold line.
It works pretty good, but the lines are not shown on the right point. See screenshot.
I think there is a problem with the domain, but I can't figure it out. Here is my script:
Update: The problem is caused by the way I position the text. Any ideas how to position the text in another way?
var svg = d3.select("svg"),
margin = {top: 20, right: 50, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var parseTime = d3.utcParse("%Y-%m-%dT%H:%M:%S.%LZ");
var bisectDate = d3.bisector(function(d) { return d.timestamp; }).left;
var formatValue = d3.format(",.2f");
d3.json("data.json", function(error, json) {
if (error) throw error;
var data = json[0].tideData;
data.forEach(function(d) {
d.timestamp = parseTime(d.timestamp);
});
var xDomain = d3.extent(data, function(d) { return d.timestamp; });
var yDomain = d3.extent(data, function(d) { return d.tide; });
var xScale = d3.scaleTime().rangeRound([0, width]).domain(xDomain);
var yScale = d3.scaleLinear().rangeRound([height, 0]).domain(yDomain);
var line = d3.line()
.defined(function(d) { return d.tide!=null; })
.x(function(d) { return xScale(d.timestamp); })
.y(function(d) { return yScale(d.tide); });
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(yScale))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.style("text-anchor", "end")
.text("Sea Level");
g.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
var focus = g.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("circle")
.attr("r", 4.5);
focus.append("text")
.attr("x", 9)
.attr("dy", ".35em");
focus.append('line')
.attr('id', 'focusLineX')
.attr('class', 'focusLine');
focus.append('line')
.attr('id', 'focusLineY')
.attr('class', 'focusLine');
g.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove);
function mousemove() {
var mouse = d3.mouse(this);
var mouseDate = xScale.invert(mouse[0]);
var i = bisectDate(data, mouseDate); // returns the index to the current data item
var d0 = data[i - 1];
var d1 = data[i];
// work out which date value is closest to the mouse
var d = mouseDate - d0[0] > d1[0] - mouseDate ? d1 : d0;
var x = xScale(d.timestamp);
var y = yScale(d.tide);
focus.attr("transform", "translate(" + x + "," + y + ")");
focus.select("text").text(formatValue(d.tide));
focus.select('#focusLineX')
.attr('x1', x).attr('y1', yScale(yDomain[0]))
.attr('x2', x).attr('y2', yScale(yDomain[1]));
focus.select('#focusLineY')
.attr('x1', xScale(xDomain[0])).attr('y1', y)
.attr('x2', xScale(xDomain[1])).attr('y2', y);
}
});
Upvotes: 0
Views: 1003
Reputation: 1017
The problem is the transformation of the whole focus variable. I positioned the circle and the text separately and now it works:
// focus tracking
var focus = g.append('g').style('display', 'none')
focus.append('circle')
.attr('id', 'focusCircle')
.attr('r', 4.5)
.attr('class', 'circle focusCircle')
focus.append("text")
.attr("x", 9)
.attr("dy", ".35em")
focus.append('line')
.attr('id', 'focusLineX')
.attr('class', 'focusLine')
focus.append('line')
.attr('id', 'focusLineY')
.attr('class', 'focusLine')
g.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() { focus.style("display", null) })
.on("mouseout", function() { focus.style("display", "none") })
.on("mousemove", mousemove)
function mousemove() {
var mouse = d3.mouse(this)
var mouseDate = xScale.invert(mouse[0])
var i = bisectDate(data, mouseDate) // returns the index to the current data item
var d0 = data[i - 1]
var d1 = data[i]
// work out which date value is closest to the mouse
var d = mouseDate - d0[0] > d1[0] - mouseDate ? d1 : d0
var x = xScale(d.date)
var y = yScale(d.tide)
focus.select("text")
.attr("transform", "translate(" + x + "," + y + ")")
.text(formatValue(d.tide))
focus.select('#focusCircle')
.attr('cx', x)
.attr('cy', y)
focus.select('#focusLineX')
.attr('x1', xScale(d.date)).attr('y1', yScale(yDomain[0]))
.attr('x2', xScale(d.date)).attr('y2', yScale(yDomain[1]))
focus.select('#focusLineY')
.attr('x1', xScale(xDomain[0])).attr('y1', yScale(d.tide))
.attr('x2', xScale(xDomain[1])).attr('y2', yScale(d.tide))
}
Upvotes: 1
Reputation: 128
In the code where you are creating the focus lines, you are using the current data point(mouse position) to start your lines.
focus.select('#focusLineX')
.attr('x1', x).attr('y1', yScale(yDomain[0]))
.attr('x2', x).attr('y2', yScale(yDomain[1]));
focus.select('#focusLineY')
.attr('x1', xScale(xDomain[0])).attr('y1', y)
.attr('x2', xScale(xDomain[1])).attr('y2', y);
You horizontal line should start from y axis and span over the width at the hight of your point. Similarly the vertical line should start at top of graph and go till bottom(x axis).
Try this change
focus.select('#focusLineX')
.attr('x1', x).attr('y1', 0)
.attr('x2', x).attr('y2', heightOfChart);
focus.select('#focusLineY')
.attr('x1', 0).attr('y1', y)
.attr('x2', widthOfChart).attr('y2', y);
Hope it helps
Upvotes: 0