I am beginner in D3 and don't know a lot about it.
I am currently trying to plot a 2D line on a graph using D3. I have been successfully able to plot the line, add axis , and even add zoom capabilities to the plot.
I am stuck in making the line hinged to y axis such that you can't just click on it and move it left or right (Currently if you would run the jsfiddle you could click on it and move the plotted line). I would still want to zoom in or out . But it should remain hinged to the y axis. Please let me know if this clarifies my issue.
Thanks!
Link to JS fiddle:
https://jsfiddle.net/adityap16/4sts8nfs/2/
Code
var data = [{
"mytime": "2015-12-01T23:10:00.000Z",
"value": 64
}, {
"mytime": "2015-12-01T23:15:00.000Z",
"value": 67
}, {
"mytime": "2015-12-01T23:20:00.000Z",
"value": 70
}, {
"mytime": "2015-12-01T23:25:00.000Z",
"value": 64
}, {
"mytime": "2015-12-01T23:30:00.000Z",
"value": 72
}, {
"mytime": "2015-12-01T23:35:00.000Z",
"value": 75
}, {
"mytime": "2015-12-01T23:40:00.000Z",
"value": 71
}, {
"mytime": "2015-12-01T23:45:00.000Z",
"value": 80
}, {
"mytime": "2015-12-01T23:50:00.000Z",
"value": 83
}, {
"mytime": "2015-12-01T23:55:00.000Z",
"value": 86
}, {
"mytime": "2015-12-02T00:00:00.000Z",
"value": 80
}, {
"mytime": "2015-12-02T00:05:00.000Z",
"value": 85
}];
var parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse;
data.forEach(function(d) {
d.mytime = parseDate(d.mytime);
});
//var margin = { top: 30, right: 30, bottom: 40, left:50 },
var margin = {
top: 30,
right: 30,
bottom: 40,
left: 50
},
height = 200,
width = 900;
var color = "green";
var xaxis_param = "mytime";
var yaxis_param = "value";
var params1 = {
margin: margin,
height: height,
width: width,
color: color,
xaxis_param: xaxis_param,
yaxis_param: yaxis_param
};
draw_graph(data, params1);
function draw_graph(data, params) {
var make_x_axis = function() {
return d3.svg.axis()
.scale(x_scale)
.orient("bottom")
.ticks(5);
};
var make_y_axis = function() {
return d3.svg.axis()
.scale(y_scale)
.orient("left")
.ticks(5);
};
//Get the margin
var xaxis_param = params.xaxis_param;
var yaxis_param = params.yaxis_param;
var color_code = params.color;
var margin = params.margin;
var height = params.height - margin.top - margin.bottom,
width = params.width - margin.left - margin.right;
var x_extent = d3.extent(data, function(d) {
return d[xaxis_param]
});
var y_extent = d3.extent(data, function(d) {
return d[yaxis_param]
});
var x_scale = d3.time.scale()
.domain(x_extent)
.range([0, width]);
var y_scale = d3.scale.linear()
.domain([0, y_extent[1]])
.range([height, 0]);
var zoom = d3.behavior.zoom()
.x(x_scale)
.y(y_scale)
.on("zoom", zoomed);
//Line
var line = d3.svg.line()
.defined(function(d) {
return d[yaxis_param];
})
.x(function(d) {
return x_scale(d[xaxis_param]);
})
.y(function(d) {
return y_scale(d[yaxis_param]);
});
var lineRef = d3.svg.line()
.x(function(d) {
return x_scale(d[xaxis_param]);
})
.y(function(d) {
return y_scale(20);
});
var myChart = d3.select('body').append('svg')
.attr('id', 'graph')
.style('background', '#E7E0CB')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')')
.call(zoom);
myChart.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr("class", "plot");
var legend = myChart.append("g")
.attr("class", "legend")
.attr("transform", "translate(" + 5 + "," + (height - 25) + ")")
legend.append("rect")
.style("fill", color_code)
.attr("width", 20)
.attr("height", 20);
legend.append("text")
.text(yaxis_param)
.attr("x", 25)
.attr("y", 12);
var vAxis = d3.svg.axis()
.scale(y_scale)
.orient('left')
.ticks(5)
var hAxis = d3.svg.axis()
.scale(x_scale)
.orient('bottom')
.ticks(5);
var majorAxis = d3.svg.axis()
.scale(x_scale)
.orient('bottom')
.ticks(d3.time.day, 1)
.tickSize(-height)
.outerTickSize(0);
myChart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(hAxis);
myChart.append("g")
.attr("class", "x axis major")
.attr("transform", "translate(0," + height + ")")
.call(majorAxis);
myChart.append("g")
.attr("class", "y axis")
.call(vAxis);
var circlePoint = myChart.selectAll('circle')
.data(data)
.enter()
.append("circle");
var circleAttributes = circlePoint
.attr("cx", function (d) { return x_scale(d[xaxis_param]); })
.attr("cy", function (d) { return y_scale(d[yaxis_param]); })
.attr("r", 3)
.style("fill", "none")
.style("stroke", "red");
var clip = myChart.append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height);
var chartBody = myChart.append("g")
.attr("clip-path", "url(#clip)");
chartBody.append("svg:path")
.datum(data)
.attr('class', 'line')
.attr("d", line)
.attr('stroke', color_code)
.attr('stroke-width', 1)
.attr('fill', 'none');
chartBody
.append('svg:path')
.datum(data)
.attr('class', 'line1')
.attr("d", lineRef)
.attr('stroke', 'blue')
.attr('stroke-width', 1)
.style("stroke-dasharray", ("3, 3"))
.attr('fill', 'none');
function zoomed() {
myChart.select(".x.axis").call(hAxis);
myChart.select(".y.axis").call(vAxis);
myChart.select(".x.axis.major").call(majorAxis);
myChart.select(".line")
.attr("class", "line")
.attr("d", line);
myChart.select(".line1")
.attr("class", "line1")
.attr("d", lineRef);
}
}
Add this .on("mousedown.zoom", null) to disable panning:
var myChart = d3.select('body').append('svg')
.attr('id', 'graph')
.style('background', '#E7E0CB')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')')
.call(zoom)
.on("mousedown.zoom", null);//disable panning
working code here
Related
I am having trouble creating a chart with a simple line.
I'll put here my code and an image of how the line is getting off axis Y and X. I really have no idea why this is happening.
Chart:
HTML:
<div id="myChart"></div>
JavaScript:
Function to adjust my json only with the data I need:
var reduceVendedores = vendedores.reduce(function (allSales, sales) {
if (allSales.some(function (e) {
return e.vendnm === sales.vendnm;
})) {
allSales.filter(function (e) {
return e.vendnm === sales.vendnm
})[0].Vendas_Ano += sales.Vendas_Ano;
allSales.filter(function (e) {
return e.vendnm === sales.vendnm
})[0].Vendas_Ant += sales.Vendas_Ant
} else {
allSales.push({
vendnm: sales.vendnm,
Vendas_Ano: sales.Vendas_Ano,
Vendas_Ant: sales.Vendas_Ant
})
}
return allSales;
}, []);
Defining X and Y Axes of the Chart:
var margin = { top: 30, right: 30, bottom: 70, left: 60 },
width = 600 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
function getMax() {
return reduceVendedores.map(d => d.Vendas_Ano)
}
var svg = d3.select("#myChart")
.append("svg")
.attr("width", width)
.attr("height", height);
var xscale = d3.scaleBand()
.range([0, width - 100])
.domain(reduceVendedores.map(function (d) { return d.vendnm; }))
var yscale = d3.scaleLinear()
.domain([0, d3.max(getMax()) + 30000])
.range([height / 2, 0]);
var x_axis = d3.axisBottom().scale(xscale);
var y_axis = d3.axisLeft().scale(yscale);
svg.append("g")
.attr("transform", "translate(50, 10)")
.call(y_axis);
var xAxisTranslate = height / 2 + 10;
svg.append("g")
.attr("transform", "translate(50, " + xAxisTranslate + ")")
.call(d3.axisBottom(xscale))
.selectAll("text")
.attr("transform", "translate(-10,0)rotate(-45)")
.style("text-anchor", "end");
Defining the chart line:
svg.append("path")
.datum(reduceVendedores)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("d", d3.line()
.x(function (d) { return xscale(d.vendnm); })
.y(function (d) { return yscale(d.Vendas_Ano) })
)
You're not adjusting for the margins you gave to your axis with:
.attr("transform", "translate(50, 10)")
Try:
svg.append('g')
.attr('transform','translate(50,0)')
.append("path")
.datum(reduceVendedores)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("d", d3.line()
.x(function (d) { return xscale(d.vendnm); })
.y(function (d) { return yscale(d.Vendas_Ano) })
)
Typically you'd set your margin in svg, like:
var svg = d3.select("#myChart")
.append("svg")
.attr("width", width)
.attr("height", height)
.append('g')
.attr('transform','translate(' + margin.left + ',' + margin.top + ')')
But doing so know would ruin the alignment of your axis.
I'm building three line graphs in d3.js and I would like that when the viewer hovers over a section of the line graph, they are able to see the country, date and incidents. Currently all that is being returned is an undefined. any help would be immensely appreciated.
Here is my html:
<div id="callOut">
<div id="divCountry"></div>
<div id="divDate"></div>
<div id="divIncidents"></div>
</div>
Here are my helper functions:
function circleHover(chosen) {
if (modus == "incidents") {
d3.select("#callOut")
.style("top", "570px")
.style("left", "30px");
d3.select("#divCountry").html('Country: ' + chosen.country);
d3.select("#divDate").html('Date: ' + chosen.date);
d3.select("#divIncidents").html('Incidents: ' + chosen.incidents);
d3.select("#callOut")
.style("visibility","visible");
}
};
function showOne(d) {
var margin = {top: 80, right: 80, bottom: 80, left: 80},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var svg = d3.select("#svg").append("svg")
.attr("width", width)
.attr("height", height);
var incidentsData = svg.append("g");
incidentsData.selectAll(".incidentsData")
.data(incidentData)
.enter()
.append('incidentsData')
.attr('class', 'incidentsData');
var chosen = d;
var setOpacity = 0;
if (modus == "incidents") {
circleHover(chosen);
setOpacity = 0.1;
incidentsData.selectAll(".incidentsData")
.filter(function (d) { return eval("d.country") !=
eval("chosen.country");})
.style("fill-opacity", setOpacity);
}
}
};
Here is a snapshot of my incidentsData:
var incidentData =
[
{
"country": "Israel",
"date": 1971,
"incidents": 1,
"": ""
},
{
"country": "Israel",
"date": 1972,
"incidents": 15,
"": ""
},
Here is my totalIncidentsLineGraph:
function totalIncidentsLineGraph() {
modus = "incidents";
var margin = {top: 80, right: 80, bottom: 80, left: 80},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var svg = d3.select("#svg").append("svg")
.attr("width", width)
.attr("height", height);
var incidentsData = svg.append("g");
incidentsData.selectAll(".incidentsData")
.data(incidentData)
.enter()
.append('incidentsData')
.attr('class', 'incidentsData')
.attr('id', function(d) { return d.country, d.date,
d.incidents});
var parse = d3.time.format("%Y").parse;
var x = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]),
xAxis = d3.svg.axis().scale(x).tickSize(-height).tickSubdivide(true),
yAxis = d3.svg.axis().scale(y).ticks(4).orient("right");
var area = d3.svg.area()
.interpolate("monotone")
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.incidents); });
var line = d3.svg.line()
.interpolate("monotone")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.incidents); });
function type(d) {
d.country = d.country;
d.date = parse(d.date);
d.incidents = +d.incidents
return d;
};
var data = d3.csv("static/data/israel/incidents_linechart.csv", type,
function(error, data) {
console.log(data);
var israel = data.filter(function(d) {
return d.country == "Israel";
});
var unitedStates = data.filter(function(d) {
return d.country == "United States";
});
var iran = data.filter(function(d) {
return d.country == "Iran";
});
x.domain([unitedStates[0].date, unitedStates[unitedStates.length -
1].date]);
y.domain([0, d3.max(egypt, function(d) { return d.incidents;
})]).nice();
var svg = d3.select("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top +
")")
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + width + ",0)")
.call(yAxis);
svg.append("text")
.attr("id", "titleLine")
.attr("y", -50)
.attr("x", 250)
.attr("dy", ".35em")
.style("font-size", "12px")
.text("Total Incidents Amongst Countries: 1970 - 2017");
var colors = d3.scale.category10();
svg.selectAll('.line')
.data([israel, unitedStates, iran, saudiArabia, turkey, egypt, syria,
lebanon, jordan, palestine]) /// can add however many i want here
.enter()
.append('path')
.attr('class', 'line')
.style('stroke', function(d) {
return colors(Math.random() * 50);
})
.attr('clip-path', 'url(#clip)')
.attr('d', function(d) {
return line(d);
})
.on("mouseover", showOne)
.on("mouseout", showAll);
Current:
Desired:
The following bar graph consists of two horizontal bars. One of them represents the time span for which an employee was scheduled to work for the day. The second represents the time that the employee actually worked. The second bar is fractured into 3 separate sections based on the data (work_start_1-work_end_1/a gap where the employee took a break/work_start_2-work_end_2). The employee happened to work longer than they were scheduled. How do I make the corresponding area on the second bar (worked) red as to alert that the employee has worked too long? This is proving difficult with D3.
<script src="moment.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css"/>
var all_moments = [];
var scheduled_start = moment("2015-03-25 08:00:00");
var scheduled_end = moment("2015-03-25 17:00:00");
var worked_start_1 = moment("2015-03-25 08:00:00");
var worked_end_1 = moment("2015-03-25 12:00:00");
var worked_start_2 = moment("2015-03-25 13:00:00");
var worked_end_2 = moment("2015-03-25 19:00:00");
all_moments.push(scheduled_start);
all_moments.push(scheduled_end);
all_moments.push(worked_start_1);
all_moments.push(worked_end_1);
all_moments.push(worked_start_2);
all_moments.push(worked_end_2);
var earliest_moment = moment.min(all_moments);
var latest_moment = moment.min(all_moments);
var data=[
{"action": "Scheduled", "tooltipTitle": "Scheduled","gap": false,"to": scheduled_end,"from": scheduled_start},
{"action": "Worked", "tooltipTitle": "Worked", "gap": false,"to": worked_end_1,"from": worked_start_1},
{"action": "Worked", "tooltipTitle": "Gap", "gap": true, "to": worked_start_2,"from": worked_end_1},
{"action": "Worked", "tooltipTitle": "Worked", "gap": false, "to": worked_end_2,"from": worked_start_2}
]
var margin = {top: 50, right: 50, bottom: 50, left: 100},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var y = d3.scale.ordinal().rangeRoundBands([0, height], .08);
var x = d3.time.scale()
.domain([earliest_moment,latest_moment])
.range([0,width]);
y.domain(data.map(function(d) { return d.action; }));
x.domain([d3.min(data,function(d){return d.from;}), d3.max(data,function(d){return d.to;})]);
var customTimeFormat = d3.time.format("%I:%M:%p");
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(10)
.tickFormat(customTimeFormat);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
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 + ")");
svg.append("g")
.attr("class", "x axis")
.style("font", "14px times")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("x", width-75)
.attr("dx", ".71em")
.attr("dy", "-.71em");
//.text("Temperatures (C)");
svg.append("g")
.attr("class", "y axis")
.style("font", "18px times")
.style('font-family', '"Open Sans", sans-serif')
.call(yAxis);
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("y", function(d) { return y(d.action); })
.attr("height", y.rangeBand())
.attr("x", function(d) { return x(d.from); })
.attr("width", function(d) { return x(d.to)-x(d.from) })
.style("fill", function(d,i) {
if(d.action == "Worked" && d.gap == false) {
return d3.rgb("#76ff03");
}
else if(d.action == "Worked" && d.gap == true) {
return d3.rgb("#e0e0e0");
}
else {
return d3.rgb("#00e5ff");
}
});
var tooltip = d3.select("body")
.append('div')
.attr('class', 'tooltip');
tooltip.append('div')
.attr('class', 'tooltipTitle');
tooltip.append('div')
.attr('class', 'timeRange');
svg.selectAll(".bar")
.on('mouseover', function(d) {
//console.log(m1.format("MMM Do YY"));
tooltip.select('.tooltipTitle').html("<b>" + d.tooltipTitle + "</b>");
tooltip.select('.timeRange').html( d.from.format("LT") + " to " + d.to.format("LT"));
tooltip.style('display', 'block');
tooltip.style('opacity',2);
})
.on('mousemove', function(d) {
tooltip.style('top', (d3.event.layerY + 10) + 'px')
.style('left', (d3.event.layerX - 25) + 'px');
})
.on('mouseout', function() {
tooltip.style('display', 'none');
tooltip.style('opacity',0);
});
How do I make the corresponding area on the second bar (worked) red as to alert that the employee has worked too long?
This can be achieved by using linear gradients, where you set the stops based on the width of the schedules times, and then apply the fill to the actual worked time bars
The gradient is set up with a stop using the 'to' value from the first object in the array, assuming that this will always be your 'scheduled' time. The gradient uses userSpaceOnUse so that uses the coordiates from the SVG container, rather than each rect it is applied to.
let gradient = svg.append("defs")
.append("linearGradient")
.attr("gradientUnits", "userSpaceOnUse")
.attr("id", "gradient")
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", width)
.attr("y2", 0);
let offset = x(data[0].to) / width
gradient.append('stop').attr("offset", 0).attr("stop-color", 'green')
gradient.append('stop').attr("offset", offset).attr("stop-color", 'green')
gradient.append('stop').attr("offset", offset).attr("stop-color", 'red')
gradient.append('stop').attr("offset", 1).attr("stop-color", 'red')
Then the gradient is applied as the fill on the rects:
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("y", function(d) { return y(d.action); })
.attr("height", y.rangeBand())
.attr("x", function(d) { return x(d.from); })
.attr("width", function(d) { return x(d.to)-x(d.from) })
.style("fill", function(d,i) {
if(d.action == "Worked" && d.gap == false) {
return 'url(#gradient)';
}
else if(d.action == "Worked" && d.gap == true) {
return d3.rgb("#e0e0e0");
}
else {
return d3.rgb("#00e5ff");
}
var all_moments = [];
var scheduled_start = moment("2015-03-25 08:00:00");
var scheduled_end = moment("2015-03-25 17:00:00");
var worked_start_1 = moment("2015-03-25 08:00:00");
var worked_end_1 = moment("2015-03-25 12:00:00");
var worked_start_2 = moment("2015-03-25 13:00:00");
var worked_end_2 = moment("2015-03-25 19:00:00");
all_moments.push(scheduled_start);
all_moments.push(scheduled_end);
all_moments.push(worked_start_1);
all_moments.push(worked_end_1);
all_moments.push(worked_start_2);
all_moments.push(worked_end_2);
var earliest_moment = moment.min(all_moments);
var latest_moment = moment.min(all_moments);
var data=[
{"action": "Scheduled", "tooltipTitle": "Scheduled","gap": false,"to": scheduled_end,"from": scheduled_start},
{"action": "Worked", "tooltipTitle": "Worked", "gap": false,"to": worked_end_1,"from": worked_start_1},
{"action": "Worked", "tooltipTitle": "Gap", "gap": true, "to": worked_start_2,"from": worked_end_1},
{"action": "Worked", "tooltipTitle": "Worked", "gap": false, "to": worked_end_2,"from": worked_start_2}
]
var margin = {top: 50, right: 50, bottom: 50, left: 100},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var y = d3.scale.ordinal()
.rangeRoundBands([0, height], .08);
var x = d3.time.scale()
.domain([earliest_moment,latest_moment])
.range([0,width]);
y.domain(data.map(function(d) { return d.action; }));
x.domain([d3.min(data,function(d){return d.from;}), d3.max(data,function(d){return d.to;})]);
var customTimeFormat = d3.time.format("%I:%M:%p");
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(10)
.tickFormat(customTimeFormat);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
let gradient = svg.append("defs")
.append("linearGradient")
.attr("gradientUnits", "userSpaceOnUse")
.attr("id", "gradient")
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", width)
.attr("y2", 0);
let offset = x(data[0].to) / width
gradient.append('stop').attr("offset", 0).attr("stop-color", 'green')
gradient.append('stop').attr("offset", offset).attr("stop-color", 'green')
gradient.append('stop').attr("offset", offset).attr("stop-color", 'red')
gradient.append('stop').attr("offset", 1).attr("stop-color", 'red')
svg.append("g")
.attr("class", "x axis")
.style("font", "14px times")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("x", width-75)
.attr("dx", ".71em")
.attr("dy", "-.71em");
//.text("Temperatures (C)");
svg.append("g")
.attr("class", "y axis")
.style("font", "18px times")
.style('font-family', '"Open Sans", sans-serif')
.call(yAxis);
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("y", function(d) { return y(d.action); })
.attr("height", y.rangeBand())
.attr("x", function(d) { return x(d.from); })
.attr("width", function(d) { return x(d.to)-x(d.from) })
.style("fill", function(d,i) {
if(d.action == "Worked" && d.gap == false) {
return 'url(#gradient)';
}
else if(d.action == "Worked" && d.gap == true) {
return d3.rgb("#e0e0e0");
}
else {
return d3.rgb("#00e5ff");
}
});
var tooltip = d3.select("body")
.append('div')
.attr('class', 'tooltip');
tooltip.append('div')
.attr('class', 'tooltipTitle');
tooltip.append('div')
.attr('class', 'timeRange');
svg.selectAll(".bar")
.on('mouseover', function(d) {
//console.log(m1.format("MMM Do YY"));
tooltip.select('.tooltipTitle').html("<b>" + d.tooltipTitle + "</b>");
tooltip.select('.timeRange').html( d.from.format("LT") + " to " + d.to.format("LT"));
tooltip.style('display', 'block');
tooltip.style('opacity',2);
})
.on('mousemove', function(d) {
tooltip.style('top', (d3.event.layerY + 10) + 'px')
.style('left', (d3.event.layerX - 25) + 'px');
})
.on('mouseout', function() {
tooltip.style('display', 'none');
tooltip.style('opacity',0);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.js"></script>
I am new D3 and i am trying to draw multiple plots based on incoming data. Here i have tried plotting one of them and will add 4 more like these on the same page.
Following is my code so far:
var data = [
{"mytime": "2015-12-01T11:10:00.000Z", "value": 64},
{"mytime": "2015-12-01T11:15:00.000Z", "value": 67},
{"mytime": "2015-12-01T11:20:00.000Z", "value": 70},
{"mytime": "2015-12-01T11:25:00.000Z", "value": 64},
{"mytime": "2015-12-01T11:30:00.000Z", "value": 72},
{"mytime": "2015-12-01T11:35:00.000Z", "value": 75},
{"mytime": "2015-12-01T11:40:00.000Z", "value": 71},
{"mytime": "2015-12-01T11:45:00.000Z", "value": 80}
];
var margin = { top: 30, right: 30, bottom: 40, left:50 };
var height = 200 - margin.top - margin.bottom,
width = 800 - margin.left - margin.right;
console.log("1")
var parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse;
data.forEach(function(d) {
d.mytime = parseDate(d.mytime);
});
var x_extent = d3.extent(data, function(d){
return d.mytime});
console.log("2")
var y_extent = d3.extent(data, function(d){
return d.value});
var x_scale = d3.time.scale()
.domain(x_extent)
.range([0,width]);
console.log("3")
var y_scale = d3.scale.linear()
.domain([0,y_extent[1]])
.range([height,0]);
//Line
var lineGen = d3.svg.line()
.x(function (d) {
console.log(d.date)
return x_scale(d.mytime);
})
.y(function (d) {
return y_scale(d.value);
});
var myChart = d3.select('body').append('svg')
.style('background', '#E7E0CB')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate('+ margin.left +', '+ margin.top +')')
.append('svg:path')
.datum(data)
.attr('class', 'line')
.attr("d",lineGen)
.attr("data-legend","pulse")
.attr('stroke', 'green')
.attr('stroke-width', 0.5)
.attr('fill', 'none');
var vGuideScale = d3.scale.linear()
.domain([0,y_extent[1]])
.range([height, 0])
var vAxis = d3.svg.axis()
.scale(vGuideScale)
.orient('left')
// .tickFormat("") //For removing values
.ticks(5)
var vGuide = d3.select('svg').append('g')
vAxis(vGuide)
vGuide.attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')')
vGuide.selectAll('path')
.style({ fill: 'none', stroke: "#000"})
vGuide.selectAll('line')
.style({ stroke: "#000"})
console.log("4")
d3.select('svg').append('g')
.append("text")
.attr("transform", "rotate(-90)")
.attr("y",6)
.attr("x",-50)
.attr("dy", ".51em")
.attr("font-size", "10px")
.style("text-anchor", "end")
.attr("fill", "green")
.text("Value");
console.log("5")
var hAxis = d3.svg.axis()
.scale(x_scale)
.orient('bottom')
.ticks(d3.time.minute, 5);
var hGuide = d3.select('svg').append('g')
hAxis(hGuide)
hGuide.attr('transform', 'translate(' + margin.left + ', ' + (height + margin.top) + ')')
hGuide.selectAll('path')
.style({ fill: 'none', stroke: "#000"})
hGuide.selectAll('line')
.style({ stroke: "#000"})
I have few doubts regarding formatting of the code:
How to add legend to the code when i tried the following link but i am getting error on using the call function
http://bl.ocks.org/ZJONSSON/3918369
How to reduce reduce tick size on y axis?
Thanks
The answer here is to use css to control the style of your components (including font sizes). For instance, for all fonts in your SVG tag:
svg {
font: 10px sans-serif;
}
Second, building a legend can be pretty simple:
var legend = myChart.append("g")
.attr("class", "legend")
.attr("transform", "translate(" + 5 + "," + (height - 25) + ")")
legend.append("rect")
.style("fill", "green")
.attr("width", 20)
.attr("height", 20);
legend.append("text")
.text("My Awesome Legend Item")
.attr("x", 25)
.attr("y", 12);
Full code sample:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
<style>
svg {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<script>
var data = [{
"mytime": "2015-12-01T11:10:00.000Z",
"value": 64
}, {
"mytime": "2015-12-01T11:15:00.000Z",
"value": 67
}, {
"mytime": "2015-12-01T11:20:00.000Z",
"value": 70
}, {
"mytime": "2015-12-01T11:25:00.000Z",
"value": 64
}, {
"mytime": "2015-12-01T11:30:00.000Z",
"value": 72
}, {
"mytime": "2015-12-01T11:35:00.000Z",
"value": 75
}, {
"mytime": "2015-12-01T11:40:00.000Z",
"value": 71
}, {
"mytime": "2015-12-01T11:45:00.000Z",
"value": 80
}];
var margin = {
top: 30,
right: 30,
bottom: 40,
left: 50
};
var height = 200 - margin.top - margin.bottom,
width = 800 - margin.left - margin.right;
console.log("1")
var parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse;
data.forEach(function(d) {
d.mytime = parseDate(d.mytime);
});
var x_extent = d3.extent(data, function(d) {
return d.mytime
});
console.log("2")
var y_extent = d3.extent(data, function(d) {
return d.value
});
var x_scale = d3.time.scale()
.domain(x_extent)
.range([0, width]);
console.log("3")
var y_scale = d3.scale.linear()
.domain([0, y_extent[1]])
.range([height, 0]);
//Line
var lineGen = d3.svg.line()
.x(function(d) {
console.log(d.date)
return x_scale(d.mytime);
})
.y(function(d) {
return y_scale(d.value);
});
var myChart = d3.select('body').append('svg')
.style('background', '#E7E0CB')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');
myChart
.append('svg:path')
.datum(data)
.attr('class', 'line')
.attr("d", lineGen)
.attr("data-legend", "pulse")
.attr('stroke', 'green')
.attr('stroke-width', 0.5)
.attr('fill', 'none');
var legend = myChart.append("g")
.attr("class", "legend")
.attr("transform", "translate(" + 5 + "," + (height - 25) + ")")
legend.append("rect")
.style("fill", "green")
.attr("width", 20)
.attr("height", 20);
legend.append("text")
.text("My Awesome Legend Item")
.attr("x", 25)
.attr("y", 12);
var vGuideScale = d3.scale.linear()
.domain([0, y_extent[1]])
.range([height, 0])
var vAxis = d3.svg.axis()
.scale(vGuideScale)
.orient('left')
// .tickFormat("") //For removing values
.ticks(5)
var vGuide = d3.select('svg').append('g').attr("class", "y axis")
vAxis(vGuide)
vGuide.attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')')
console.log("4")
d3.select('svg').append('g')
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("x", -50)
.attr("dy", ".51em")
.attr("font-size", "10px")
.style("text-anchor", "end")
.attr("fill", "green")
.text("Value");
console.log("5")
var hAxis = d3.svg.axis()
.scale(x_scale)
.orient('bottom')
.ticks(d3.time.minute, 5);
var hGuide = d3.select('svg').append('g').attr("class", "x axis")
hAxis(hGuide)
hGuide.attr('transform', 'translate(' + margin.left + ', ' + (height + margin.top) + ')')
</script>
</body>
</html>
You can control the length of the ticks via the tickSize setter
Like this:
var axis = d3.svg.axis()
.scale(scale)
.orient("right")
.ticks(2)
.tickSize(10)
.tickFormat(formatPercent);
and then:
svg.append("g")
.attr("class", "y axis")
.call(axis);
I tried the D3 focus + context graph with some sorted data but the path line overlaps itself. It works properly if I use linear interpolation but when I try to smoothen the graph using monotone or cardinal interpolation, it overlaps. Any clues??
Code:
var margin = {top: 10, right: 10, bottom: 100, left: 40},
margin2 = {top: 430, right: 10, bottom: 20, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
height2 = 500 - margin2.top - margin2.bottom;
var parseDate = function(d){return new Date(d);};
var x = d3.time.scale().range([0, width]),
x2 = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]),
y2 = d3.scale.linear().range([height2, 0]);
var xAxis = d3.svg.axis().scale(x).orient("bottom"),
xAxis2 = d3.svg.axis().scale(x2).orient("bottom"),
yAxis = d3.svg.axis().scale(y).orient("left");
var brush = d3.svg.brush()
.x(x2)
.on("brush", brushed);
var area = d3.svg.area()
.interpolate("monotone")
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.price); });
var area2 = d3.svg.area()
.interpolate("monotone")
.x(function(d) { return x2(d.date); })
.y0(height2)
.y1(function(d) { return y2(d.price); });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
function check(data) {
x.domain(d3.extent(data.map(function(d) { return d.date; })));
y.domain([0, d3.max(data.map(function(d) { return d.price; }))]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "y axis")
.call(yAxis);
context.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area2);
context.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height2 + 7);
};
function brushed() {
x.domain(brush.empty() ? x2.domain() : brush.extent());
focus.select(".area").attr("d", area);
focus.select(".x.axis").call(xAxis);
}
function type(d) {
d.date = parseDate(d.time);
d.price = +d.count;
return d;
}
data = [
{
"count": 1,
"time": 1387152000000
},
{
"count": 1,
"time": 1390780800000
},
{
"count": 5,
"time": 1390867200000
},
{
"count": 2,
"time": 1390953600000
},
{
"count": 1,
"time": 1391040000000
}];
check(data.map(function(r){return type(r);}));
JSFiddle:
http://jsfiddle.net/v6G8J/4/