D3.js moving a line and a circle after a button press - javascript

The JSFiddle of my code is at this URL http://jsfiddle.net/b8eLJ/1/
Within the code, I have implemented a back button. The objective is that the calendar on x-axis should go back in time. As this happens, the lines should also move back in time.
I have the x-axis working correctly with the new domain - but am struggling with the lines and circles.
I have also looked at the following example http://bl.ocks.org/mbostock/1166403
I am a little confused about how to either "select the line and move it" or "destroy the line and recreate it". I have tried both and neither seem to work.
function startFunction() {
console.log("start");
endDate.setDate(endDate.getDate() - 7);
startDate.setDate(startDate.getDate() - 7);
//change the domain to reflect the new dates
xScale.domain([startDate, endDate]);
var t = svg.transition().duration(750);
t.select(".x.axis").call(xAxis);
//???
var pl = svg.selectAll("path.line");
pl.exit().remove();
}
// INPUT
dataset2 = [{
movie: "test",
results: [{
week: "20140102",
revenue: "5"
}, {
week: "20140109",
revenue: "10"
}, {
week: "20140116",
revenue: "17"
}, ]
}, {
movie: "test",
results: [{
week: "20140206",
revenue: "31"
}, {
week: "20140213",
revenue: "42"
}]
}];
console.log("1");
var parseDate = d3.time.format("%Y%m%d").parse;
var lineFunction = d3.svg.line()
.x(function (d) {
return xScale(parseDate(String(d.week)));
})
.y(function (d) {
return yScale(d.revenue);
})
.interpolate("linear");
console.log("2");
var endDate = new Date();
var startDate = new Date();
startDate.setDate(startDate.getDate() - 84);
//SVG Width and height
var margin = {
top: 20,
right: 10,
bottom: 20,
left: 40
};
var w = 750 - margin.left - margin.right;
var h = 250 - margin.top - margin.bottom;
//X SCALE AND AXIS STUFF
//var xMin = 0;
//var xMax = 1000;
var xScale = d3.time.scale()
.domain([startDate, endDate])
.range([0, w]);
console.log(parseDate("20130101"));
console.log("3");
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(12);
console.log("4S");
//Y SCALE AND AXIS STUFF
//max and min test
var minY = d3.min(dataset2, function (kv) {
return d3.min(kv.results, function (d) {
return +d.revenue;
})
});
var maxY = d3.max(dataset2, function (kv) {
return d3.max(kv.results, function (d) {
return +d.revenue;
})
});
console.log("min y " + minY);
console.log("max y " + maxY);
var yScale = d3.scale.linear()
.domain([minY, maxY])
.range([h, 0]);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(10);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
console.log("4S1");
//CREATE X-AXIS
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (h) + ")")
.call(xAxis);
//Create Y axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(,0)")
.call(yAxis);
//create circle
var movie_groups = svg.selectAll("g.metric_group")
.data(dataset2).enter()
.append("g")
.attr("class", "metric_group");
var circles = movie_groups.selectAll("circle")
.data(function (d) {
return d.results;
});
svg.selectAll("g.circle")
.data(dataset2)
.enter()
.append("g")
.attr("class", "circle")
.selectAll("circle")
.data(function (d) {
return d.results;
})
.enter()
.append("circle")
.attr("cx", function (d) {
// console.log(d[0]);
console.log(parseDate(d.week));
return xScale(parseDate(d.week));
})
.attr("cy", function (d) {
return yScale(d.revenue);
})
.attr("r", 3);
//create line
//create line
var lineGraph = svg.selectAll("path.line")
.data(dataset2).enter().append("path")
.attr("d", function (d) {
return lineFunction(d.results);
})
.attr("class", "line");

To clarify, lines moving back in time will actually translate to lines moving forward to the right since their date is not changing and the time axis is moving to the right. I hope I am thinking clear here...it is late.
Importantly, your data is not changing between clicks so there will be no new data to be bound in the enter() selection after the initial call. Lines will be drawn once and only once.
Since your axis is the movable part, one quick solution would be to keep removing the lines and rebuilding them again inside startFunction, like this:
var lineGraph = svg.selectAll("path.line").remove();
lineGraph = svg.selectAll("path.line")
.data(dataset2);
I tried this and it works. Note however that I am not removing the lines off of the exit selection and thus not truly leveraging the EUE pattern (Enter/Update/Exit). But, since your data is not really changing, I am not sure what you could add to it between clicks that could be used as the key to selection.data. I hope this helps...
NOTE: I am not particularly fond of this solution and considered writing it as a comment but there is too much verbiage. For one thing, object constancy is lost since we veered away from the EUE pattern and the "graph movement" is ugly. This can be mitigated some by adding a transition delay in the drawing of lines (path) as shown below...but still...
lineGraph.enter().append("path").transition().delay(750)
.attr("d", function (d) {
return lineFunction(d.results);
})

Related

Find min date within data to draw svg using d3.js

Im using d3.js to visualise some data, and I'm brand new to both Javascript and SVG etc.
I've got the following code which does work
var margin = {top: 20, right: 30, bottom: 30, left: 140},
width = pbi.width - margin.left - margin.right,
height = pbi.height - margin.top - margin.bottom;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.ordinal()
.rangeRoundBands([0, height], 0.1, 0.2);
var svg = d3.select("#chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var header = svg.append("g")
.attr("id", "header");
// Title
header.append("text")
.attr("class", "title")
.attr("x", 10)
.attr("y", 15)
.text("The visual");
pbi.dsv(function(Data) {
var mindate = new Date('01-01-1905');
var maxdate = new Date();
x.domain([mindate, maxdate]).nice(d3.time.month);
y.domain(Data.map(function(d) { return d.milestone; }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.svg.axis().scale(x).orient("bottom"));
svg.append("g")
.attr("class", "y axis")
.call(d3.svg.axis().scale(y).orient("left"));
svg.append("g")
.selectAll(".circle")
.data(Data)
.enter().append("circle")
.attr("cx", function(d) {return x(new Date(d.date))} )
.attr("cy", function(d) {return y(d.milestone)+33; })
.attr("r", 10);
});
What I want to do next is alter the mindate line so it dynamically finds the lowest date in the dataset. As of now, its hardcoded.
Something like var mindate = new Date(d3.min(d.date)) but that definitely doesn't work
My data is formatted as follows:
{reportingyear:'2016', owner:'***', thing:'a-thing', milestone:'start-date', date:'01/12/10'},
{reportingyear:'2017', owner:'***', thing:'a-thing', milestone:'start-date', date:'01/12/10'},
{reportingyear:'2020', owner:'***', thing:'a-thing', milestone:'start-date', date:'01/12/10'}
etc. (De-identified for security reasons!)
Any tips to point me in the right direction is greatly appreciated.
Thank you.
After applying the help below, I know am running into the following error
This code is being developed for use in PowerBI in the d3.js plug in, so the data is actually going to be pulled from the data model, the list data is just to replicate the svg in browser to edit.
First, if you've got data represented as a list of objects with a date field expressed as a string, you should consider converting those strings to Date objects. It's not immediately clear from your example what the date format is but, assuming it's month/day/year, you could perform that task as
data = data.map(function (o) {
o.date = new Date(o.date);
return o;
})
Then, you could compute the min as
d3.min(data, o => o.date)
Here's complete code:
let data = [
{
reportingyear: "2016",
owner: "***",
thing: "a-thing",
milestone: "start-date",
date: "01/12/10"
},
{
reportingyear: "2017",
owner: "***",
thing: "a-thing",
milestone: "start-date",
date: "01/12/10"
},
{
reportingyear: "2020",
owner: "***",
thing: "a-thing",
milestone: "start-date",
date: "01/12/10"
}
].map(function (o) {
o.date = new Date(o.date);
return o;
});
let min_date = d3.min(data, o => o.date)
console.log(min_date)
<script src="https://d3js.org/d3.v3.min.js"></script>

d3 area stacked line chart

I'm working on modifying this stacked line chart example: https://bl.ocks.org/d3indepth/e4efd402b4d9fdb2088ccdf3135745c3
I'm adding a time x axis, but I'm struggling with this block of code:
var areaGenerator = d3.area()
.x(function(d, i) {
// return i * 100;
return i * 253.5;
})
.y0(function(d) {
return y(d[0]);
})
.y1(function(d) {
return y(d[1]);
});
The original example has the .x accessor as i * 100 which seems to be a random value. When I add the X axis the stacked line chart does not line up correctly with the date ticks. I can manually force it to line up by returning i * 253.5 but that is not ideal. I don't really understand how this area function is working - any help would be appreciated.
let height = 600;
let width = 800;
const yMax = 4000;
//var hEach = 40;
let margin = {top: 20, right: 15, bottom: 25, left: 25};
width = width - margin.left - margin.right;
height = height - margin.top - margin.bottom;
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 + ")");
let formatDate = d3.timeFormat("%b-%Y")
let parseTime = d3.timeParse("%Y-%m-%d");
let data = [
{
"host_count": 2553,
"container_count": 875,
"hour": "2019-01-31",
"apm_host_count": 0,
"agent_host_count": 2208,
"gcp_host_count": 0,
"aws_host_count": 345
},
{
"host_count": 1553,
"container_count": 675,
"hour": "2019-02-01",
"apm_host_count": 0,
"agent_host_count": 1208,
"gcp_host_count": 0,
"aws_host_count": 445
},
{
"host_count": 716,
"container_count": 6234,
"hour": "2019-02-02",
"apm_host_count": 0,
"agent_host_count": 479,
"gcp_host_count": 0,
"aws_host_count": 237
},
{
"host_count": 516,
"container_count": 4234,
"hour": "2019-02-03",
"apm_host_count": 0,
"agent_host_count": 679,
"gcp_host_count": 0,
"aws_host_count": 137
}
];
// format the data
data.forEach(function(d) {
d.hour = parseTime(d.hour);
});
// set the ranges
var x = d3.scaleTime().range([0, width]);
x.domain(d3.extent(data, function(d) { return d.hour; }));
var xAxis = d3.axisBottom(x).ticks(11).tickFormat(d3.timeFormat("%y-%b-%d")).tickValues(data.map(d=>d.hour));
var y = d3.scaleLinear()
.domain([0, yMax])
.range([height, 0]);
var areaGenerator = d3.area()
.x(function(d, i) {
console.log(d);
return i * 100;
})
.y0(function(d) {
return y(d[0]);
})
.y1(function(d) {
return y(d[1]);
});
var colors = ['#FBB65B', '#513551', '#de3163']
var stack = d3.stack()
.keys(['agent_host_count', 'aws_host_count', 'container_count']);
var stackedSeries = stack(data);
d3.select('g')
.selectAll('path')
.data(stackedSeries)
.enter()
.append('path')
.style('fill', function(d, i) {
return colors[i];
})
.attr('d', areaGenerator)
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
<!doctype html>
<html lang="en">
<head>
<title>Usage</title>
</head>
<body>
<div id="svg"></div>
<script src="https://d3js.org/d3.v5.min.js"></script>
</body>
</html>
When using axes in d3, you actually need to use the axis variable to calculate scaling factors. The functions that do this calculation are returned by the scale*() methods. In your code you have this for the x-axis:
var x = d3.scaleTime().range([0, width]);
As such, the variable x now contains a function that will do interpolation for you. this is what your areaGenerator function should look like:
var areaGenerator = d3.area()
.x(function(d, i) {
return x(d.data.hour);
})
.y0(function(d) {
return y(d[0]);
})
.y1(function(d) {
return y(d[1]);
});
The only thing you need to remember is that when calculating the value you need to use the same variable that the axis is based on. I.e. your x-axis is a time axis so you need to calculate the interpolation using the time variable (d.data.hour).
As to where 100 comes from in the example, you are essentially correct. In that block the value of 100 is more or less arbitrary. It was likely chosen because the chart looks reasonably good at that scale. By choosing 100, each "tick" is spaced 100px apart and since there is no x-axis to be judged against it doesn't actually matter what is used as long as it changes for each data point.

Scattered plot zooming not working correctly in d3.js

I am trying to create a scatterplot with a zooming. The following code I have used in my angular application and its working till a certain extent when I run it in my local server. However putting the same code in Stackblitz, the zooming is not working. I want to achieve a zooming where the zooming is limited to just the values on the graph. There should be no zooming of the axis accept the value changes in both the axis. Something exactly like : http://bl.ocks.org/peterssonjonas/4a0e7cb8d23231243e0e .
Here in the example, on zooming, only the values are zoomed and the axis values changed correspondingly. It doesn't zoom the whole graph plot area. How do I achieve it? Here is my Stackblitz code:
https://stackblitz.com/edit/angular-hu2thj
ANSWER :
Finally I figure out the graph for this problem in case of any future reference:
import { Component, OnInit, Input, ViewChild, ElementRef } from '#angular/core';
import * as d3 from 'd3';
#Component({
selector: 'app-scatterplot',
templateUrl: './scatterplot.component.html',
styleUrls: ['./scatterplot.component.css']
})
export class ScatterplotComponent implements OnInit {
#ViewChild('chart1') private chartContainer: ElementRef;
dataValue = [{ x: "67", y: "188", },
{ x: "200", y: "163" },
{ x: "254", y: "241" },
{ x: "175", y: "241" },
];
ngOnInit() {
this.graph();
}
graph() {
const element = this.chartContainer.nativeElement;
var svgWidth = 400;
var svgHeight = 400;
var margin = { top: 30, right: 40, bottom: 50, left: 60 };
var width = svgWidth - margin.left - margin.right;
var height = svgHeight - margin.top - margin.bottom;
var originalCircle = {
"cx": -150,
"cy": -15,
"r": 20
};
var svgViewport = d3.select(element)
.append('svg')
.attr('width', svgWidth)
.attr('height', svgHeight)
// create scale objects
var x = d3.scaleLinear()
.domain([1, 500])
.range([0, width]);
var y = d3.scaleLinear()
.domain([1, 500])
.range([height, 0]);
// create axis objects
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
// Zoom Function
var zoom = d3.zoom()
.on("zoom", zoomFunction);
// Inner Drawing Space
var innerSpace = svgViewport.append("g")
.attr("class", "inner_space")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
// append some dummy data
var data = innerSpace.selectAll("circle")
.data(this.dataValue)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", function (d) {
return x(d.x)
;
})
.attr("cy", function (d) {
return y(d.y);
})
.attr("r", 2);
// Draw Axis
var gX = innerSpace.append("g")
.attr("class", "axis")
.attr("transform", "translate(0, " + height + ")")
.call(xAxis);
var gY = innerSpace.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
// append zoom area
var view = innerSpace.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height - 10)
.attr("fill", "transparent")
.attr("fill-opacity", 0.1)
.call(zoom)
function zoomFunction() {
// create new scale ojects based on event
var new_xScale = d3.event.transform.rescaleX(x)
var new_yScale = d3.event.transform.rescaleY(y)
console.log(d3.event.transform)
// update axes
gX.call(xAxis.scale(new_xScale));
gY.call(yAxis.scale(new_yScale));
// update circle
data.attr("transform", d3.event.transform)
};
}
}
The problem on stackblitz is that d3.event is null.
Try this to zoom the points in your local server.
You need to add a clip path and animate the axis, see the second example (heatmap)
var svg = d3.select(element)
.append("svg:svg")
.attr("width", w + left_padding)
.attr("height", h + top_padding);
var g = svg.append("g");
var zoom = d3.zoom().on("zoom", function () {
console.log("zoom", d3, d3.event);
g.attr("transform", d3.event.transform);
});
svg.call(zoom);
g.selectAll("circle")
.data(this.dataValue)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", d => x(d.x) )
.attr("cy", d => y(d.y) )
.attr("r", 2);

D3 multi-line chart - Error: <path> attribute d: Expected number, "MNaN,NaNLNaN,NaNC…"

I'm tring to create a multi-line chart using D3.js. Here is my sample csv data:
A,B,C,D,E,F,G,date
53831,72169.87,54219,72555,63466,115312,126390,4/26/16
53031,70901.11,5976,5111,62388,111626,123198,7/10/16
51834,69917.12,5449,4902,62990,114296,124833,4/24/16
54637,73016.92,58535,77379,63090,113216,125261,6/14/16
54801,73072.4,57997,75674,63090,113216,125261,6/27/16
53578,71718.19,51085,69152,63370,115061,125949,5/3/16
51679,68897.14,6021,5421,61514,110330,121972,7/24/16
Here is my code snippet. However I keep seeing the error like d is not an expected number(as title shows). Can anyone please point me out?
Also I feel like the way I'm parsing data is ugly (two for loop). Any suggestions are welcome.
// Set the dimensions of the canvas / graph
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
},
width = 800 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%b %Y").parse;
// 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");
var yAxis = d3.svg.axis().scale(y)
.orient("left");
var line = d3.svg.line()
.interpolate("basis")
.x(function (d) {
return x(d.date);
})
.y(function (d) {
return y(d.value);
});
// Adds the svg canvas
var svg = d3.select("#d3-line-chart")
.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 + ")");
//get the data
d3.csv("test.csv", function (error, data) {
var res = [];
var cols = d3.keys(data[0])
.filter(function (key) {
return key;
});
for (var j = 0; j < cols.length - 1; j++) {
var col = cols[j];
var row = [];
for (var i = 0; i < data.length; i++) {
row.push({
symbol: col,
date: data[i]["date"],
value: +data[i][col]
});
}
res.push(row);
}
// Scale the range of the data
x.domain(d3.extent(res, function (d) {
return d.date;
}));
y.domain([0, d3.max(res, function (d) {
return d.value;
})]);
svg.selectAll(".line")
.data(res)
.enter().append("path")
.attr("class", "line")
.attr("d", line);
// 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);
});
Firstly, provide sorted data in the CSV on the basis of date so:
Instead of this:
A,B,C,D,E,F,G,date
53831,72169.87,54219,72555,63466,115312,126390,4/26/16
53031,70901.11,5976,5111,62388,111626,123198,7/10/16
51834,69917.12,5449,4902,62990,114296,124833,4/24/16
54637,73016.92,58535,77379,63090,113216,125261,6/14/16
54801,73072.4,57997,75674,63090,113216,125261,6/27/16
53578,71718.19,51085,69152,63370,115061,125949,5/3/16
51679,68897.14,6021,5421,61514,110330,121972,7/24/16
Provide sorted CSV:
A,B,C,D,E,F,G,date
51834,69917.12,5449,4902,62990,114296,124833,4/24/16
53831,72169.87,54219,72555,63466,115312,126390,4/26/16
53578,71718.19,51085,69152,63370,115061,125949,5/3/16
54637,73016.92,58535,77379,63090,113216,125261,6/14/16
54801,73072.4,57997,75674,63090,113216,125261,6/27/16
53031,70901.11,5976,5111,62388,111626,123198,7/10/16
51679,68897.14,6021,5421,61514,110330,121972,7/24/16
Secondly:
The date parser you providing is incorrect:
var parseDate = d3.time.format("%b %Y").parse;
Should have been this:
var parseDate = d3.time.format("%m/%d/%Y").parse;
Because your date is in the format ,4/26/16.
Thirdly,
The way you are calculating the x and y domain extent is wrong:
So instead of this:
// Scale the range of the data
x.domain(d3.extent(res, function (d) {
return d.date;
}));
y.domain([0, d3.max(res, function (d) {
return d.value;
})]);
It should have been:
x.domain(d3.extent(data, function (d) {
return parseDate(d.date);
}));
y.domain([0, d3.max(res, function (d) {
return d3.max(d, function(d2){console.log(d2);return d2.value;});
})]);
Reason: the res array you creating is an array inside an array so need that handling in here.
Working code here

I am trying to visualize my json object with D3. I want date to be the x axis and y to be sales. number values stored a string

I have a json object that I am trying to visualize with D3.js. I want the x axis to represent the date in the json object which is stored as a string and the y axis to represent sales projections which is also a number in a string i.e "85,000.00"
example of my json object:
[{"Num":78689,"Client":"Health Services" ,"TotalEstSales":"85,000,000.00","Date ":"2/15/2015","RFP Receipt Date":null,"Exp. Proposal Due Date":"3/6/2015","Proposal Submission Date":null,"estAwardDate":"4/15/2015","Procurement Type":"New - Incumbent","Bid Type":"Standalone Contract"}]
and my d3 code:
// Various accessors that specify the four dimensions of data to visualize.
function x(d) { return d.date; }
function y(d) { return d.TotalEstSales; }
function radius(d) { return parseFloat(d.TotalEstSales);}
function color(d) { return d.region; }
function key(d) { return d.Title;}
// Chart dimensions.
var margin = {top: 19.5, right: 19.5, bottom: 19.5, left: 39.5},
width = 960 - margin.right,
height = 500 - margin.top - margin.bottom;
// Various scales. These domains make assumptions of data, naturally.
var xScale = d3.scale.log().domain([300, 1e5]).range([0, width]),
yScale = d3.scale.linear().domain([10000, 85000000]).range([height, 0]),
radiusScale = d3.scale.sqrt().domain([0, 5e8]).range([0, 40]),
colorScale = d3.scale.category10();
// The x & y axes.
var xAxis = d3.svg.axis().orient("bottom").scale(xScale).ticks(12, d3.format(",d")),
yAxis = d3.svg.axis().scale(yScale).orient("left");
// Create the SVG container and set the origin.
var svg = d3.select("#chart").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 + ")");
// 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);
// Add an x-axis label.
svg.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.attr("x", width)
.attr("y", height - 6)
.text("Data of RFP");
// Add a y-axis label.
svg.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("y", 6)
.attr("dy", ".75em")
.attr("transform", "rotate(-90)")
.text("Award amount");
// Add the year label; the value is set on transition.
var label = svg.append("text")
.attr("class", "year label")
.attr("text-anchor", "end")
.attr("y", height - 24)
.attr("x", width)
.text(2015);
// Load the data.
d3.json("rfpdata.json", function(data) {
// A bisector since many nation's data is sparsely-defined.
// var bisect = d3.bisector(function(d) { return d[0]; });
// Add a dot per nation. Initialize the data at 1800, and set the colors.
var dot = svg.append("g")
.attr("class", "dots")
.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.style("fill", function(d) { return colorScale(color(d)); })
.call(position)
.sort(order);
// Add a title.
dot.append("title")
.text(function(d) { return d.Client; })
// Positions the dots based on data.
function position(dot) {
dot .attr("cx", function(d) { return xScale(x(d)); })
// .attr("cy", function(d) { return yScale(y(d)); })
.attr("r", function(d) { return radiusScale(radius(d)); });
}
// Defines a sort order so that the smallest dots are drawn on top.
function order(a, b) {
return radius(b) - radius(a);
}
// After the transition finishes, you can mouseover to change the year.
function enableInteraction() {
var yearScale = d3.scale.linear()
.domain([1800, 2009])
.range([box.x + 10, box.x + box.width - 10])
.clamp(true);
// Cancel the current transition, if any.
function mouseover() {
label.classed("active", true);
}
function mouseout() {
label.classed("active", false);
}
function mousemove() {
displayYear(yearScale.invert(d3.mouse(this)[0]));
}
}
// this is the function needed to bring in data
// Interpolates the dataset for the given (fractional) year.
function interpolateData(date) {
return data.map(function(d) {
return {
title: d.Title,
client: d.Client,
sales: parseFloat(d.TotalEstSales),
sales: interpolateValues(d.TotalEstSales, date),
};
});
}
// Finds (and possibly interpolates) the value for the specified year.
function interpolateValues(values, date) {
var i = bisect.left(values, date, 0, values.length - 1),
a = values[i];
if (i > 0) {
var b = values[i - 1],
t = (date - a[0]) / (b[0] - a[0]);
return a[1] * (1 - t) + b[1] * t;
}
return a[1];
}
});
I am not sure what I am doing wrong but the data is not displaying? Am i properly parsing the date string? This was a graph available on the d3 site. I want a bubble graph where the radius changes depending on the size of the sale and the date is on the x axis.
#all Update:
I was able to make the proper adjustment for date on the xaxis here:
var xAxis = d3.svg.axis().orient("bottom").scale(xScale).tickFormat(d3.time.format("%m/%d")),
yAxis = d3.svg.axis().scale(yScale).orient("left").ticks(23, d3.format(" ,d"));
d3.time.format was what I was looking for. Once data was loaded I needed to parse the date:
month = data.Date;
parseDate = d3.time.format("%m/%d/%Y").parse;
data.forEach(function(d) {
d.Date = parseDate(d.Date);
});
// update Dates here when new report comes in monthly
xScale.domain([parseDate("1/1/2015"),parseDate("6/1/2015")]);
obviously, using "Date" as a name column in the excel file was not idea for "Date" in js(because it is an oject).

Categories

Resources