Offset Line stroke-weight d3.js - javascript

I'm using d3.js to plot a highway network over a map SVG. I'd like to be able to vary the stroke-weight of the line to illustrate demand based on a value.
Highway links are define as one way, so for example a two way road would have two overlapping line elements (with separate id's). I can use stroke-weight to edit the thickness of the line based on a variable (as below), but on a two way road, the larger of the two stroke weights will always cover the smaller rendering it invisible.
Is there an easy way to offset a line by half its stroke-weight to the left hand side of the direction the line is drawn? (direction denoted by x1,y1 x2,y2)
d3.csv("links.csv", function (error, data) {
d3.select("#lines").selectAll("line")
.data(data)
.enter()
.append("line")
.each(function (d) {
d.p1 = projection([d.lng1, d.lat1]);
d.p2 = projection([d.lng2, d.lat2]);
})
.attr("x1", function (d) { return d.p1[0]; })
.attr("y1", function (d) { return d.p1[1]; })
.attr("x2", function (d) { return d.p2[0]; })
.attr("y2", function (d) { return d.p2[1]; })
.on('mouseover', tip_link.show)
.on('mouseout', tip_link.hide)
.style("stroke", "black")
.style("stroke-width", lineweight)
});

One option would be to just create new start/end points when drawing your lines and use those:
var offset = function(start,destination,distance) {
// find angle of line
var dx = destination[0] - start[0];
var dy = destination[1] - start[1];
var angle = Math.atan2(dy,dx);
// offset them:
var newStart = [
start[0] + Math.sin(angle-Math.PI)*distance,
start[1] + Math.cos(angle)*distance
];
var newDestination = [
destination[0] + Math.sin(angle-Math.PI)*distance,
destination[1] + Math.cos(angle)*distance
];
// return the new start/end points
return [newStart,newDestination]
}
This function takes two points and offsets them by a particular amount given the angle between the two points. Negative values shift to the other side, swapping the start and destination points will shift to the other side.
In action, this looks like, with the original line in black:
var offset = function(start,destination,distance) {
// find angle of line
var dx = destination[0] - start[0];
var dy = destination[1] - start[1];
var angle = Math.atan2(dy,dx);
// offset them:
var newStart = [
start[0] + Math.sin(angle-Math.PI)*distance,
start[1] + Math.cos(angle)*distance
];
var newDestination = [
destination[0] + Math.sin(angle-Math.PI)*distance,
destination[1] + Math.cos(angle)*distance
];
// return the new start/end points
return [newStart,newDestination]
}
var line = [
[10,10],
[200,100]
];
var svg = d3.select("svg");
// To avoid repetition:
function draw(selection) {
selection.attr("x1",function(d) { return d[0][0]; })
.attr("x2",function(d) { return d[1][0]; })
.attr("y1",function(d) { return d[0][1]; })
.attr("y2",function(d) { return d[1][1]; })
}
svg.append("line")
.datum(line)
.call(draw)
.attr("stroke","black")
.attr("stroke-width",1)
svg.append("line")
.datum(offset(...line,6))
.call(draw)
.attr("stroke","orange")
.attr("stroke-width",10)
svg.append("line")
.datum(offset(...line,-4))
.call(draw)
.attr("stroke","steelblue")
.attr("stroke-width",5)
<svg width="500" height="300"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
You will need to adapt this to your data structure, and it requires twice as many lines as before, because you aren't using stroke width, your using lines. This is advantageous if you wanted to use canvas.

Related

Create a line passing through some points with d3.js

I have a situation very similar to the one in this JSFiddle with some points representing a team (in particular its final rank in a football season).
I would like to substitute the points with a line passing exactly in these points so that the final result shows the temporal evolution of each team in terms of final ranking position.
I know how to create a line by setting the X1,X2,Y1,Y2 coordinates but I don't understand how to set this coordinates to the exact value (e.g. if the line is between season 2006-2007 and season 2007-2008 I will have to set X1 and Y1 with value from the first season as d[0] and d[1] but for X2 and Y2 I need values from the next element in the array.
I'm very new with D3.js so any advice and solution is very welcome. Thanks
Assuming you have already declared some data for your lines drawing the actual lines based on that data is as simple as this:
create the X and Y scales:
var xScale = d3.scale.linear().domain([dataRange.x1, dataRange.x2]).range([plotRange.x1, plotRange.x2]);
var yScale = d3.scale.linear().domain([dataRange.y1, dataRange.y2]).range([plotRange.y1, plotRange.y2]);
declare the line function:
var valueLine = d3.svg.line()
.x(function (dataItem, arrayIndex) {
return xScale(dataItem);
})
.y(function (dataItem, arrayIndex) {
return yScale(dataItem)
});
and finally create the path:
g.append("path")
.style("stroke", someColour)
.attr("d", valueLine(myData))
.attr("class", "someClass");
Refer to more documentation here: https://www.dashingd3js.com/
Based on that fiddle, this is what I'd do:
First, I'd set a class to each team's circles (team1, team2 and so on...). So, I could later retrieve the circles' values for each team.
For retrieving the circles values, I'd use a for loop:
for(var j = 1; j < 4; j++){//this loops from "Team1" to "Team3"
var team = d3.selectAll("circle.Team" + j)[0];//selects the team by class
for(var i = 0; i < team.length; i++){//this loops through the circles
if(team[i+1]){//if the next circle exists
svg.append("line")
.attr("x1", d3.select(team[i]).attr("cx"))//this circle
.attr("y1", d3.select(team[i]).attr("cy"))//this circle
.attr("x2", d3.select(team[i+1]).attr("cx"))//the next circle
.attr("y2", d3.select(team[i+1]).attr("cy"))//the next circle
.attr("stroke", function(){
return _TEAM_COLORS_["Team" + j]
});//sets the colours based on your object
}
}
};
Here is that fiddle, updated: https://jsfiddle.net/gerardofurtado/6cc0ehz2/18/

venn.js selecting Set[A] except Set[B]

I have this code:
var sets = [
{sets: ['A'], size: 10},
{sets: ['B'], size: 10},
{sets: ['A','B'], size: 5}
];
var chart = venn.VennDiagram();
var div = d3.select("#venn").datum(sets).call(chart);
using excellent venn.js library, my venn diagram is drawn and works perfectly.
using this code:
div.selectAll("g")
.on("mouseover", function (d, i) {
// sort all the areas relative to the current item
venn.sortAreas(div, d);
// Display a tooltip with the current size
tooltip.transition().duration(400).style("opacity", .9);
tooltip.text(d.size + " items");
// highlight the current path
var selection = d3.select(this).transition("tooltip").duration(400);
selection.select("path")
.style("stroke-width", 3)
.style("fill-opacity", d.sets.length == 1 ? .4 : .1)
.style("stroke-opacity", 1)
.style("cursor", "pointer");
})
.on("mousemove", function () {
tooltip.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("click", function (d, i) {
window.location.href = "/somepage"
})
.on("mouseout", function (d, i) {
tooltip.transition().duration(400).style("opacity", 0);
var selection = d3.select(this).transition("tooltip").duration(400);
selection.select("path")
.style("stroke-width", 1)
.style("fill-opacity", d.sets.length == 1 ? .25 : .0)
.style("stroke-opacity", 0);
});
I'm able to add Click, mouseover,... functionality to my venn.
Here is the problem:
Adding functionality to Circles (Sets A or B) works fine.
Adding functionality to Intersection (Set A intersect Set B) works fine.
I need to add some functionality to Except Area (set A except set B)
This question helped a little: 2D Polygon Boolean Operations with D3.js SVG
But I had no luck making this work.
Tried finding out Except area using: clipperjs or Greiner-Hormann polygon clipping algorithm but couldn't make it work.
Update 1:
The code in this question is copied from venn.js sample: http://benfred.github.io/venn.js/examples/intersection_tooltip.html
Other samples:
https://github.com/benfred/venn.js/
Perhaps you can do something like this....
Given 2 overlapping circles,
Find the two intersection points, and
Manually create a path that arcs from IP1 to IP2 along circle A and then from IP2 back to IP1 along circle B.
After that path is created (that covers A excluding B), you can style it however you want and add click events (etc.) to that SVG path element.
FIND INTERSECTION POINTS (IPs)
Circle-circle intersection points
var getIntersectionPoints = function(circleA, circleB){
var x1 = circleA.cx,
y1 = circleA.cy,
r1 = circleA.r,
x2 = circleB.cx,
y2 = circleB.cy,
r2 = circleB.r;
var d = Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2)),
a = (Math.pow(r1,2)-Math.pow(r2,2)+Math.pow(d,2))/(2*d),
h = Math.sqrt(Math.pow(r1,2)-Math.pow(a,2));
var MPx = x1 + a*(x2-x1)/d,
MPy = y1 + a*(y2-y1)/d,
IP1x = MPx + h*(y2-y1)/d,
IP1y = MPy - h*(x2-x1)/d,
IP2x = MPx - h*(y2-y1)/d,
IP2y = MPy + h*(x2-x1)/d;
return [{x:IP1x,y:IP1y},{x:IP2x,y:IP2y}]
}
MANUALLY CREATE PATH
var getExclusionPath = function(keepCircle, excludeCircle){
IPs = getIntersectionPoints(keepCircle, excludeCircle);
var start = `M ${IPs[0].x},${IPs[0].y}`,
arc1 = `A ${keepCircle.r},${keepCircle.r},0,1,0,${IPs[1].x},${IPs[1].y}`,
arc2 = `A ${excludeCircle.r},${excludeCircle.r},0,0,1,${IPs[0].x},${IPs[0].y}`,
pathStr = start+' '+arc1+' '+arc2;
return pathStr;
}
var height = 900;
width = 1600;
d3.select(".plot-div").append("svg")
.attr("class", "plot-svg")
.attr("width", "100%")
.attr("viewBox", "0 0 1600 900")
var addCirc = function(circ, color){
d3.select(".plot-svg").append("circle")
.attr("cx", circ.cx)
.attr("cy", circ.cy)
.attr("r", circ.r)
.attr("fill", color)
.attr("opacity", "0.5")
}
var getIntersectionPoints = function(circleA, circleB){
var x1 = circleA.cx,
y1 = circleA.cy,
r1 = circleA.r,
x2 = circleB.cx,
y2 = circleB.cy,
r2 = circleB.r;
var d = Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2)),
a = (Math.pow(r1,2)-Math.pow(r2,2)+Math.pow(d,2))/(2*d),
h = Math.sqrt(Math.pow(r1,2)-Math.pow(a,2));
var MPx = x1 + a*(x2-x1)/d,
MPy = y1 + a*(y2-y1)/d,
IP1x = MPx + h*(y2-y1)/d,
IP1y = MPy - h*(x2-x1)/d,
IP2x = MPx - h*(y2-y1)/d,
IP2y = MPy + h*(x2-x1)/d;
return [{x:IP1x,y:IP1y},{x:IP2x,y:IP2y}]
}
var getExclusionPath = function(keepCircle, excludeCircle){
IPs = getIntersectionPoints(keepCircle, excludeCircle);
var start = `M ${IPs[0].x},${IPs[0].y}`,
arc1 = `A ${keepCircle.r},${keepCircle.r},0,1,0,${IPs[1].x},${IPs[1].y}`,
arc2 = `A ${excludeCircle.r},${excludeCircle.r},0,0,1,${IPs[0].x},${IPs[0].y}`,
pathStr = start+' '+arc1+' '+arc2;
return pathStr;
}
var circleA = {cx: 600, cy: 500, r: 400};
var circleB = {cx: 900, cy: 400, r: 300};
var pathStr = getExclusionPath(circleA, circleB)
addCirc(circleA, "steelblue");
addCirc(circleB, "darkseagreen");
d3.select(".plot-svg").append("text")
.text("Hover over blue circle")
.attr("font-size", 70)
.attr("x", 30)
.attr("y", 70)
d3.select(".plot-svg").append("path")
.attr("class","exlPath")
.attr("d", pathStr)
.attr("stroke","steelblue")
.attr("stroke-width","10")
.attr("fill","white")
.attr("opacity",0)
.plot-div{
width: 50%;
display: block;
margin: auto;
}
.plot-svg {
border-style: solid;
border-width: 1px;
border-color: green;
}
.exlPath:hover {
opacity: 0.7;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div class="plot-div">
</div>
If you have more complex overlapping in your Venn diagrams (3+ region overlap) then this obviously gets more complicated, but I think you could still extend this approach for those situations.
Quick (sort of) note on to handle 3 set intersections ie. A∩B\C or A∩B∩C
There are 3 "levels" of overlap between A∩B and circle C...
Completely contained in C | Both AB IPs are in C
Partial overlap; C "cuts through" A∩B | Only one AB IP is in C
A∩B is completely outside C | No AB IPs are in C
Note: This is assuming C is not a subset of or fully contained by A or B -- otherwise, for example, both BC IPs could be contained in A
In total, you'll need 3 points to create the path for the 3 overlapping circles. The first 2 are along C where it "cuts through" A∩B. Those are...
The BC intersection point contained in A
The AC intersection point contained in B
For the 3rd point of the path, it depends if you want (i)A∩B∩C or (ii)A∩B\C...
(i) A∩B∩C: The AB intersection point contained in C
(ii) A∩B\C: The AB intersection point NOT contained in C
With those the points you can draw the path manually with the appropriate arcs.
Bonus -- Get ANY subsection for 2 circles
It's worth noting as well that you can get any subsection by choosing the right large-arc-flag and sweep-flag. Picked intelligently and you'll can get...
Circle A (as path)
Circle B (as path)
A exclude B --in shown example
B exclude A
A union B
A intersect B
... as well as a few more funky ones that won't match anything useful.
Some resources...
W3C site for elliptical curve commands
Good explanation for arc flags
Large-arc-flag: A value of 0 means to use the smaller arc, while a value of 1 means use the larger arc.
Sweep-flag: The sweep-flag determines whether to use an arc (0) or its reflection around the axis (1).

Draw multiple circles using d3.js in a 3x3 format

I am trying to draw 9 circles in a 3x3 format using d3.js .
Here is my script:-
<script src="//d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="//ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.min.js"></script>
<div class="barChart"></div>
<div class="circles"></div>
<style>
</style>
<script>
$(document).ready(function () {
circles();
$(".circles").show();
function circles() {
var svg = d3.select(".circles").append("svg");
var data = ["Z","Z","Z","Z","Z","Z","Z","Z","Z"];
var groups = svg.selectAll("g")
.data(data).attr("width",100).attr("height",100)
.enter()
.append("g");
groups.attr("transform", function(d, i) {
var x = 100;
console.log(i);
var y = 50 * i + 100 ;
return "translate(" + [x,y] + ")";
});
var circles = groups.append("circle")
.attr({cx: function(d,i){
return 0;
},
cy: function(d,i){
return 0;}})
.attr("r", "20")
.attr("fill", "red")
.style("stroke-width","2px");
var label = groups.append("text")
.text(function(d){
return d;
})
.style({
"alignment-baseline": "middle",
"text-anchor": "middle",
"font-family":"Arial",
"font-size":"30",
"fill":"white"
});
}
});
But , I am just getting some half circles.
And also , I tried to have only one value in the data and tried to run the circles() in a loop but I got circles in a straight line with lot of spacing between them
for (var i = 0; i <=8 ;i++){
circles();
}
And
var groups = svg.selectAll("g")
.data("Z").attr("width",100).attr("height",100)
.enter()
.append("g");
If I follow the second method, I got circles in a straight line and with lot of spacing.
So how to get the circles like in the figure and with less spacing ?
Can anyone please help me
Your transform function is positioning your elements in a line instead of a grid. If you log out what you are translating you will see whats happening. i.e.
console.log("translate(" + [x,y] + "");
/* OUTPUTS
translate(100,100)
translate(100,150)
translate(100,200)
translate(100,250)
translate(100,300)
translate(100,350)
translate(100,400)
translate(100,450)
translate(100,500)
translate(100,100)
*/
They are being stacked one on top of the other vertically.
You can modify the transform function by changing the following lines:
var x = 100 + (50* Math.floor(i/3));
var y = 50 * (i%3) + 20 ;
Finally, the SVG container is clipping your drawing so you are seeing only half of anything below 150px.
You can modify the width as follows:
var svg = d3.select(".circles")
.append("svg")
.attr({"height": '300px'});
The simplest way to fix your code is to modify the groups transform
groups.attr("transform", function(d, i) {
var x = (i % 3) * 100 + 200; // x varies between 200 and 500
var y = 50 * Math.floor(i / 3) + 100 ; // y varies between 100 and 250
return "translate(" + [x,y] + ")";
});
Here, i will loop between 0 and 8 because you have 8 elements in your data variable, in the x assigment, i%3 is worth 0,1,2,0,1,2,0,1,2, and in the y assignement, Math.floor(i / 3) is worth 0,0,0,1,1,1,2,2,2 so you basically get a 2D grid :
0,0 1,0 2,0
1,0 1,1 2,1
2,0 2,1 2,2

D3 cardinal line interpolation looks wrong

I'm trying to give a polygon - drawn with d3 - smooth edges using the d3.svg.line().interpolate() option but I get strange looking results.
I receive the polygon data from the nokia HERE api as world coordinate data in the form [lat1, long1, alt1, lat2, long2, alt2 ...] So in the routingCallback function - which is called when the response is in - I first refine it so it looks like this [[lat1, long1], [lat2, long2] ...]. In d3.svg.line() I then use this array of coordinates to calculate the pixel positions. Im using Leaflet to draw the polygon on a map so I use the map.latLngToLayerPoint() function to do that. The actual drawing of the polygon happens in reset() which is called from the routingCallback immediately after the data is available and every time the map gets zoomed
var map = new L.Map("map", {"center": [52.515, 13.38], zoom: 12})
.addLayer(new L.TileLayer('http://{s}.tile.cloudmade.com/---account key---/120322/256/{z}/{x}/{y}.png'));
map.on("viewreset", reset);
var svg = d3.select(map.getPanes().overlayPane).append("svg"),
g = svg.append("g").attr("class", "leaflet-zoom-hide group-element"),
bounds = [[],[]],
polygon,
refinedData,
line = d3.svg.line()
.x(function(d) {
var location = L.latLng(d[0], d[1]),
point = map.latLngToLayerPoint(location);
return point.x;
})
.y(function(d) {
var location = L.latLng(d[0], d[1]),
point = map.latLngToLayerPoint(location);
return point.y;
})
.interpolate("cardinal"),
routingCallback = function(observedRouter, key, value) {
if(value == "finished") {
var rawData = observedRouter.calculateIsolineResponse.isolines[0].asArray(),
refinedData = [];
for(var i = 2; i < rawData.length; i += 3) {
var lon = rawData[i-1],
lat = rawData[i-2];
refinedData.push([lat, lon]);
}
if(polygon)
polygon.remove();
polygon = g
.data([refinedData])
.append("path")
.style("stroke", "#000")
.style("fill", "none")
.attr("class", "isoline");
reset();
}
if(value == "failed") {
console.log(observedRouter.getErrorCause());
}
};
getIsolineData = function(isoline) {
return data;
};
function reset() {
var xExtent = d3.extent(refinedData, function(d) {
var location = L.latLng(d[0], d[1]);
var point = map.latLngToLayerPoint(location);
return point.x;
});
var yExtent = d3.extent(refinedData, function(d) {
var location = L.latLng(d[0], d[1]);
var point = map.latLngToLayerPoint(location);
return point.y;
});
bounds[0][0] = xExtent[0];
bounds[0][1] = yExtent[0];
bounds[1][0] = xExtent[1];
bounds[1][1] = yExtent[1];
var topLeft = bounds[0],
bottomRight = bounds[1];
svg .attr("width", bottomRight[0] - topLeft[0])
.attr("height", bottomRight[1] - topLeft[1])
.style("left", topLeft[0] + "px")
.style("top", topLeft[1] + "px");
g .attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")");
polygon.attr("d", line);
}
I expect this to produce smooth edges but instead I get a small loop at every corner. The red overlay is the same polygon without interpolation. There are only points at the corners. No points added inbetween.
Does it have something to do with the order of the points (clockwise/counter clockwise)? I tried to rearrange the points but nothing seemed to happen.
The only way I can recreate the pattern you're getting is if I add every vertex to the path twice. That wouldn't be noticeable with a linear interpolation, but causes the loops when the program tries to connect points smoothly.
http://fiddle.jshell.net/weuLs/
Edit:
Taking a closer look at your code, it looks like the problem is in your calculateIsolineResponse function; I don't see that name in the Leaflet API so I assume it's custom code. You'll need to debug that to figure out why you're duplicating points.
If you can't change that code, the simple solution would be to run your points array through a filter which removes the duplicated points:
refinedData = refinedData.filter(function(d,i,a){
return ( (!i) || (d[0] != a[i-1][0]) || (d[1] != a[i-1][1]) );
});
That filter will return true if either it's the first point in the array, or if either the lat or lon value is different from the previous point. Duplicated points will return false and be filtered out of the array.

D3.js linear regression

I searched for some help on building linear regression and found some examples here:
nonlinear regression function
and also some js libraries that should cover this, but unfortunately I wasn't able to make them work properly:
simple-statistics.js and this one: regression.js
With regression.js I was able to get the m and b values for the line, so I could use y = m*x + b to plot the line that followed the linear regression of my graph, but couldn't apply those values to the line generator, the code I tried is the following:
d3.csv("typeStatsTom.csv", function (error, dataset) {
//Here I plot other stuff, setup the x & y scale correctly etc.
//Then to plot the line:
var data = [x.domain(), y.domain()];
var result = regression('linear', data);
console.log(result)
console.log(result.equation[0]);
var linereg = d3.svg.line()
.x(function (d) { return x(d.Ascendenti); })
.y(function (d) { return y((result.equation[0] * d.Ascendenti) + result.equation[1]); });
var reglinepath = svg.append("path")
.attr("class", "line")
.attr("d", linereg(dataset))
.attr("fill", "none")
.attr("stroke", "#386cb0")
.attr("stroke-width", 1 + "px");
The values of result are the following in the console:
Object
equation: Array[2]
0: 1.8909425770308126
1: 0.042557422969139225
length: 2
__proto__: Array[0]
points: Array[2]
string: "y = 1.89x + 0.04"
__proto__: Object
From what I can tell in the console I should have set up the x and y values correctly, but of course the path in the resulting svg is not shown (but drawn), so I don't know what to do anymore. Any help is really really appreciated, even a solution involving the simple.statistics.js library would be helpful! Thanks!
I made it work using the following code found here:
function linearRegression(y,x){
var lr = {};
var n = y.length;
var sum_x = 0;
var sum_y = 0;
var sum_xy = 0;
var sum_xx = 0;
var sum_yy = 0;
for (var i = 0; i < y.length; i++) {
sum_x += x[i];
sum_y += y[i];
sum_xy += (x[i]*y[i]);
sum_xx += (x[i]*x[i]);
sum_yy += (y[i]*y[i]);
}
lr['slope'] = (n * sum_xy - sum_x * sum_y) / (n*sum_xx - sum_x * sum_x);
lr['intercept'] = (sum_y - lr.slope * sum_x)/n;
lr['r2'] = Math.pow((n*sum_xy - sum_x*sum_y)/Math.sqrt((n*sum_xx-sum_x*sum_x)*(n*sum_yy-sum_y*sum_y)),2);
return lr;
};
var yval = dataset.map(function (d) { return parseFloat(d.xHeight); });
var xval = dataset.map(function (d) { return parseFloat(d.Ascendenti); });
var lr = linearRegression(yval,xval);
// now you have:
// lr.slope
// lr.intercept
// lr.r2
console.log(lr);
And then plotting a line with:
var max = d3.max(dataset, function (d) { return d.OvershootingSuperiore; });
var myLine = svg.append("svg:line")
.attr("x1", x(0))
.attr("y1", y(lr.intercept))
.attr("x2", x(max))
.attr("y2", y( (max * lr.slope) + lr.intercept ))
.style("stroke", "black");
Using the code I found here
It looks to me like your path is getting drawn, just way off the screen.
Perhaps the regression is calculated incorrectly? The problem may be on line 202:
var data = [x.domain(), y.domain()];
var result = regression('linear', data);
If the raw data looks like [[1, 500], [2, 300]] this will find the linear regression of [[1, 2], [300, 500] which probably isn't what you want.
I'm guessing what you'd like to do is compute the regression with the entire set of data points rather than with the graph's bounds. Then rather than charting this line for every data value, you want to just plot the endpoints.

Categories