scatter plot points don't maintain value when zooming in d3.js - javascript

This is my first time using d3.js, so please bear with me. I am implementing this inside of a vue.js file as pure javascript.
I am trying to make a scatter plot with zooming capabilities. So far I have everything nearly working, but when I zoom I notice that the x-axis isn't scaling properly, but the y-axis is working properly. For instance, when looking at the original plot, a point may be at around 625 on the x-axis, but after zooming in the same point will be less than 600. This is not happening with the y-axis - those points scale properly. I am assuming that something is wrong with the scaling of the x-axis in my zoom function, but I just can't figure it out. Please take a look, and let me know if you can see where I went wrong.
Edit: I should mention that this is using d3.js version 7.4.4
<template>
<div id="reg_plot"></div>
</template>
<script>
import * as d3 from 'd3';
export default {
name: 'regCamGraph',
components: {
d3
},
methods: {
createSvg() {
// dimensions
var margin = {top: 20, right: 20, bottom: 30, left: 40},
svg_dx = 1400,
svg_dy =1000,
chart_dx = svg_dx - margin.right - margin.left,
chart_dy = svg_dy - margin.top - margin.bottom;
// data
var y = d3.randomNormal(400, 100);
var x_jitter = d3.randomUniform(-100, 1400);
var d = d3.range(1000)
.map(function() {
return [x_jitter(), y()];
});
// fill
var colorScale = d3.scaleLinear()
.domain(d3.extent(d, function(d) { return d[1]; }))
.range([0, 1]);
// y position
var yScale = d3.scaleLinear()
.domain(d3.extent(d, function(d) { return d[1]; }))
.range([chart_dy, margin.top]);
// x position
var xScale = d3.scaleLinear()
.domain(d3.extent(d, function(d) { return d[0]; }))
.range([margin.right, chart_dx]);
console.log("chart_dy: " + chart_dy);
console.log("margin.top: " + margin.top);
console.log("chart_dx: " + chart_dx);
console.log("margin.right: " + margin.right);
// y-axis
var yAxis = d3.axisLeft(yScale);
// x-axis
var xAxis = d3.axisBottom(xScale);
// zoom
var svg = d3.select("#reg_plot")
.append("svg")
.attr("width", svg_dx)
.attr("height", svg_dy);
svg.call(d3.zoom().on("zoom", zoom)); // ref [1]
// plot data
var circles = svg.append("g")
.attr("id", "circles")
.attr("transform", "translate(200, 0)")
.selectAll("circle")
.data(d)
.enter()
.append("circle")
.attr("r", 4)
.attr("cx", function(d) { return xScale(d[0]); })
.attr("cy", function(d) { return yScale(d[1]); })
.style("fill", function(d) {
var norm_color = colorScale(d[1]);
return d3.interpolateInferno(norm_color)
});
// add y-axis
var y_axis = svg.append("g")
.attr("id", "y_axis")
.attr("transform", "translate(75,0)")
.call(yAxis).style("font-size", "20px")
// add x-axis
var x_axis = svg.append("g")
.attr("id", "x_axis")
.attr("transform", `translate(${margin.left}, ${svg_dy - margin.bottom})`)
.call(xAxis).style("font-size", "20px")
function zoom(e) {
// re-scale y axis during zoom
y_axis.transition()
.duration(50)
.call(yAxis.scale(e.transform.rescaleY(yScale)));
// re-scale x axis during zoom
x_axis.transition()
.duration(50)
.call(xAxis.scale(e.transform.rescaleX(xScale)));
// re-draw circles using new y-axis scale
var new_xScale = e.transform.rescaleX(xScale);
var new_yScale = e.transform.rescaleY(yScale);
console.log(d);
x_axis.call(xAxis.scale(new_xScale));
y_axis.call(yAxis.scale(new_yScale));
circles.data(d)
.attr('cx', function(d) {return new_xScale(d[0])})
.attr('cy', function(d) {return new_yScale(d[1])});
}
}
},
mounted() {
this.createSvg();
}
}
</script>
Interestingly enough, after I set the clip region to prevent showing points outside of the axes the problem seemed to resolve itself. This is how I created the clip path:
// clip path
var clip = svg.append("defs").append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("id", "clip-rect")
.attr("x", "0")
.attr("y", "0")
.attr('width', chart_dx)
.attr('height', chart_dy);
And I then added that attribute to the svg when plotting the data like this:
svg.append("g").attr("clip-path", "url(#clip)")
Updated clip path with plot data section:
// clip path
var clip = svg.append("defs").append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("id", "clip-rect")
.attr("x", "0")
.attr("y", "0")
.attr('width', chart_dx)
.attr('height', chart_dy);
// plot data
var circles = svg.append("g")
.attr("id", "circles")
.attr("transform", "translate(75, 0)")
.attr("clip-path", "url(#clip)") //added here
.selectAll("circle")
.data(d)
.enter()
.append("circle")
.attr("r", 4)
.attr("cx", function(d) { return xScale(d[0]); })
.attr("cy", function(d) { return yScale(d[1]); })
.style("fill", function(d) {
var norm_color = colorScale(d[1]);
return d3.interpolateInferno(norm_color)
});

I ended up resolving this issue. I have updated the original post to show what worked for me.
Basically, after adding the clip region things started to work properly.
// clip path (this is the new clip region that I added. It prevents dots from being drawn outside of the axes.
var clip = svg.append("defs").append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("id", "clip-rect")
.attr("x", "0")
.attr("y", "0")
.attr('width', chart_dx)
.attr('height', chart_dy);
// plot data
var circles = svg.append("g")
.attr("id", "circles")
.attr("transform", "translate(75, 0)")
.attr("clip-path", "url(#clip)") //added clip region to svg here
.selectAll("circle")
.data(d)
.enter()
.append("circle")
.attr("r", 4)
.attr("cx", function(d) { return xScale(d[0]); })
.attr("cy", function(d) { return yScale(d[1]); })
.style("fill", function(d) {
var norm_color = colorScale(d[1]);
return d3.interpolateInferno(norm_color)
});

Related

Customize grid lines in d3 bar chart

I am developing a two-way bar chart using d3. I want to add grid lines to the bar chart, how to customize those grid lines. I've used d3fc to draw the grid lines. It looks something like
var x = d3.scaleLinear()
.rangeRound([0, width])
var y = d3.scaleBand()
.rangeRound([0, height]).padding(0.5);
var xAxis = d3.axisBottom(x)
.ticks(8)
.tickSize(0)
.tickFormat(function(d){
return d3.format('.00s')(Math.abs(d)); // Use Math.abs() to get the absolute value
});
var yAxis = d3.axisLeft(y)
.ticks(5)
.tickSize(0);
//draw grid lines
const gridline = fc.annotationSvgGridline()
.xScale(x)
.yScale(y);
var svg = d3.select("#ageSexNotif").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 + ")");
x.domain([-d3.max(data, function(d){
return d.female
})*1.2,d3.max(data, function(d){
return d.female
})*1.2])
y.domain(data.map(function (d) {
return d.age;
}));
svg.append("g")
.attr("class", "x axis")
.call(xAxis)
.call(gridline);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// .call(gridline);
var barsRight = svg.selectAll(".bar")
.data(data)
.enter().append("g")
barsRight.append("rect")
.attr("class", "bar")
.attr("x", function (d) {
return x(Math.min(0, d.female));
})
.attr("y", function (d) {
return y(d.age);
})
.attr("width", function (d) {
return Math.abs(x(d.female) - x(0));
})
.transition()
.duration(1500)
.delay(200)
.style("margin-top", "10px")
.attr("height", y.bandwidth())
.attr("fill", "#F293C9")
.attr("text", "label");
barsRight.append("text")
.attr("class", "label")
//y position of the label is halfway down the bar
.attr("y", function (d) {
return y(d.age) + y.bandwidth()- 6;
})
//x position is 3 pixels to the right of the bar
.attr("x", function (d) {
return x(d.female) + 10;
})
.text(function (d) {
return (d.female/1000)+'k';
})
.style("font-family", "Source Sans Pro")
.style("font-size", "14px")
.style("font-weight", "bold")
.attr("fill", "#F293C9");
var barsLeft = svg.selectAll(".bar2")
.data(data)
.enter().append("g")
barsLeft.append("rect")
.attr("class", "bar2")
.attr("x", function (d) {
return x(Math.min(0, -d.male));
})
.attr("y", function (d) {
return y(d.age);
})
.attr("width", function (d) {
return Math.abs(x(-d.male) - x(0));
})
.transition()
.duration(1500)
.delay(200)
.style("margin-top", "10px")
.attr("height", y.bandwidth())
.attr("fill", "#4880FF");
barsLeft.append("text")
.attr("class", "label")
.style("font-family", "Source Sans Pro")
.style("font-size", "14px")
.style("font-weight", "bold")
.attr("fill","#4880FF")
//y position of the label is halfway down the bar
.attr("y", function (d) {
return y(d.age) + y.bandwidth()- 6;
})
//x position is 3 pixels to the right of the bar
.attr("x", function (d) {
return x(-d.male) - 40;
})
.text(function (d) {
return (d.male/1000)+'k';
});
The result of my chart is
My chart should look like this
How to join the edges in x-axis and highlight the base axis as shown in the image? Any help for customizing the grid lines is appreciated.
Link to my example link
Thanks in advance!
You can add class name to your grid lines using attr('class', 'class-name'), and add your effect by CSS.
I've made some changes to your pen that you can see here.
The main changes:
join the edges in x-axis
If you remove the *1.2 multiplier from the domains and add .nice() then the x-axis will be squared off with the gridlines.
var x = d3.scaleLinear()
.rangeRound([0, width])
.domain([-d3.max(data, d => d.female), d3.max(data, d => d.female)])
.nice();
highlight the base axis
We can do this using an annotation line from d3fc.
const line = fc.annotationSvgLine()
.xScale(x)
.yScale(y)
.orient('vertical')
.label('')
.decorate(selection => {
selection.select('line')
.attr('stroke', 'black');
});
svg.datum([0]).call(line);
Here we are creating an annotation line using our scales. We set the line to be vertical and we remove the default label by replacing it with an empty string. After that we use the decorate function to colour the line black.
customizing the gridlines
We can control the opacity of the gridlines using a decorate function.
const opacityDecorate = selection => {
selection.attr('opacity', 0.2);
};
const gridline = fc.annotationSvgGridline()
.xScale(x)
.yScale(y)
.xDecorate(opacityDecorate)
.yDecorate(opacityDecorate);
Here we are using a decorate function to set the opacity for the both the horizontal and vertical gridlines. Alternatively you could also use different decorate functions to style the horizontal and vertical lines differently.
I hope this helps!

Creating a zoom function in D3

I have a function where that when a button is pressed (Several buttons the represent several animal types), that animal types SVG is updated with its corresponding data. I'm trying to replicate this zoom function but am having issues implementing it with my code. There are several SVGs that are used globally like this (one for each animal type):
let x = d3.scaleLinear()
.domain([0, 1000])
.range([ 0, width ]);
var xAxis = d3.axisBottom(x);
svgReptile.append("g")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
const yAxis = d3.scaleLinear()
.domain([0, 220])
.range([ height, 0])
svgReptile.append("g")
.call(d3.axisLeft(yAxis))
The function below is called when one of the animal buttons is pressed.
function update(animal, whatSVG, xAxis, yAxis, color) {
const points = whatSVG
.selectAll("circle")
.data(data);
points.enter()
.append("circle")
.attr("cx", function(d) {
return xAxis(d.state);
})
.attr("cy", function(d) {
return yAxis(d.percentage);
})
.merge(points)
.attr("r", 3)
.attr("cx", function(d) {
return xAxis(d.decade)
})
.attr("cy", function(d) {
return yAxis(d.count)
})
.style("fill", function (d) { return colour(d.animal) } );
points.exit()
.attr('r', 0)
.remove();
}
Question:
How can I implement a zoom feature that expands the x-axis when zoomed (or anything similar) like the one linked above?
I think you're looking for a 'brush zoom' from the last line of your question.
The following source code if from an example in a d3 graph gallery
The cross hair allows you to select an area to expand. If you follow the link there is a graph above it that is entitled "Zoom with axis" but it doesn't zoom in the way you've described, it just moves the axis, but doesn't enlarge the graph contents with it. Perhaps both will be useful!
Hope this helps
// set the dimensions and margins of the graph
var margin = {top: 10, right: 20, bottom: 20, left: 20},
width = 500 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var Svg = d3.select("#brushZoom")
.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 + ")");
//Read the data
d3.csv("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/iris.csv", function(data) {
// Add X axis
var x = d3.scaleLinear()
.domain([4, 8])
.range([ 0, width ]);
var xAxis = Svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 9])
.range([ height, 0]);
Svg.append("g")
.call(d3.axisLeft(y));
// Add a clipPath: everything out of this area won't be drawn.
var clip = Svg.append("defs").append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("width", width )
.attr("height", height )
.attr("x", 0)
.attr("y", 0);
// Color scale: give me a specie name, I return a color
var color = d3.scaleOrdinal()
.domain(["setosa", "versicolor", "virginica" ])
.range([ "#440154ff", "#21908dff", "#fde725ff"])
// Add brushing
var brush = d3.brushX() // Add the brush feature using the d3.brush function
.extent( [ [0,0], [width,height] ] ) // initialise the brush area: start at 0,0 and finishes at width,height: it means I select the whole graph area
.on("end", updateChart) // Each time the brush selection changes, trigger the 'updateChart' function
// Create the scatter variable: where both the circles and the brush take place
var scatter = Svg.append('g')
.attr("clip-path", "url(#clip)")
// Add circles
scatter
.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function (d) { return x(d.Sepal_Length); } )
.attr("cy", function (d) { return y(d.Petal_Length); } )
.attr("r", 8)
.style("fill", function (d) { return color(d.Species) } )
.style("opacity", 0.5)
// Add the brushing
scatter
.append("g")
.attr("class", "brush")
.call(brush);
// A function that set idleTimeOut to null
var idleTimeout
function idled() { idleTimeout = null; }
// A function that update the chart for given boundaries
function updateChart() {
extent = d3.event.selection
// If no selection, back to initial coordinate. Otherwise, update X axis domain
if(!extent){
if (!idleTimeout) return idleTimeout = setTimeout(idled, 350); // This allows to wait a little bit
x.domain([ 4,8])
}else{
x.domain([ x.invert(extent[0]), x.invert(extent[1]) ])
scatter.select(".brush").call(brush.move, null) // This remove the grey brush area as soon as the selection has been done
}
// Update axis and circle position
xAxis.transition().duration(1000).call(d3.axisBottom(x))
scatter
.selectAll("circle")
.transition().duration(1000)
.attr("cx", function (d) { return x(d.Sepal_Length); } )
.attr("cy", function (d) { return y(d.Petal_Length); } )
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div id="brushZoom"></div>

d3 Line Chart animations from left to right

I am interested to create a line chart -- that draws itself from left to right -- essentially connecting dots in the x-axis.
http://bl.ocks.org/markmarkoh/8700606
http://bl.ocks.org/duopixel/4063326
This is the jsfiddle that I've made to start creating this chart.
http://jsfiddle.net/NYEaX/1512/
//__invoke line
$('[data-role="line"]').each(function(index) {
createLine(this);
});
function createLine(el){
var w = $(el).data("width");
var h = $(el).data("height");
var svg = d3.select($(el)[0])
.append("svg")
.attr("width", w)
.attr("height", h);
var data = d3.range(11).map(function(){return Math.random()*10})
var x = d3.scale.linear().domain([0, 10]).range([0, 700]);
var y = d3.scale.linear().domain([0, 10]).range([10, 290]);
var line = d3.svg.line()
.interpolate("cardinal")
.x(function(d,i) {return x(i);})
.y(function(d) {return y(d);})
var path = svg.append("path")
.attr("d", line(data))
.attr("stroke", "steelblue")
.attr("stroke-width", "2")
.attr("fill", "none");
var totalLength = path.node().getTotalLength();
path
.attr("stroke-dasharray", totalLength + " " + totalLength)
.attr("stroke-dashoffset", totalLength)
.transition()
.duration(2000)
.ease("linear")
.attr("stroke-dashoffset", 0);
svg.on("click", function(){
path
.transition()
.duration(2000)
.ease("linear")
.attr("stroke-dashoffset", totalLength);
});
/* plot axis */
var padding = 100; // space around the chart, not including labels
// define the x axis
var xAxis = d3.svg.axis()
.orient("bottom")
.scale(x);
//.tickFormat(date_format);
// draw x axis with labels and move to the bottom of the chart area
svg.append("g")
.attr("class", "xaxis axis") // two classes, one for css formatting, one for selection below
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
/* plot axis */
}
One solution that you can do is to append svg circles that correspond with the path before drawing the path. Example Fiddle: http://jsfiddle.net/stancheta/ystymLm4/6/
var circles = svg.selectAll("dot")
.data(data)
.enter().append("svg:circle")
.attr('class', 'circ')
.attr("r", 3)
.attr("cx", function(d, i) { return x(i); })
.attr("cy", function(d, i) { return y(d); })
.style('fill', 'lightsteelblue');

d3js scatter plot auto update doesnt work

Need some help figuring out how to auto update 3djs scatter plot. The code looks fine ,however, when the update
function is running the graph gets updated but the scatter plot remains at place. I'm using svg.selectAll(".dot").remove() in order to remove the outdated ones but unable to find a way to added them back. I found a few solutions online but none of them worked for my code.
Any help would be much appreciated. thanks
DB structure:
dtg | temperature
2016-03-02 09:14:00 23
2016-03-02 09:10:00 22
Code:
<script>
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 40},
width = 400 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;
var formatTime = d3.time.format("%e %B %X");
// 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").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.dtg); })
.y(function(d) { return y(d.temperature); });
var div = d3.select("#chart1").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Adds the svg canvas
var svg = d3.select("#chart1")
.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 + ")");
function make_x_axis() {
return d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(10)
}
function make_y_axis() {
return d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10)
}
// Get the data
d3.json("2301data.php", function(error, data) {
data.forEach(function(d) {
d.dtg = parseDate(d.dtg);
d.temperature = +d.temperature;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.dtg; }));
y.domain([0, 60]); //
// y.domain([0, d3.max(data, function(d) { return d.temperature; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// draw the scatterplot
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.filter(function(d) { return d.temperature > 30 })
.style("fill", "red")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.dtg); })
.attr("cy", function(d) { return y(d.temperature); })
// Tooltip stuff after this
.on("mouseover", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
div.transition()
.duration(200)
.style("opacity", .9);
div .html(
d.temperature + "C" + "<br>" +
formatTime(d.dtg))
.style("left", (d3.event.pageX + 8) + "px")
.style("top", (d3.event.pageY - 18) + "px");})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.style("font-size", "14px")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.style("font-size", "14px")
.call(yAxis);
// Draw the grid 1
svg.append("g")
.attr("class", "grid")
.attr("transform", "translate(0," + height + ")")
.call(make_x_axis()
.tickSize(-height, 0, 0)
.tickFormat("")
)
// Draw the grid 2
svg.append("g")
.attr("class", "grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat("")
)
// Addon 3 // text label for the graph
svg.append("text")
.attr("x", (width / 2))
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "14px")
.style("text-decoration", "underline")
.style('fill', 'white')
//.attr("class", "shadow") // using text css
.text("2301 Temperature read in the past 24h\n");
});
var inter = setInterval(function() {
updateData();
}, 5000);
// ** Update data section (Called from the onclick)
function updateData() {
// Get the data again
d3.json("2301data.php", function(error, data) {
data.forEach(function(d) {
d.dtg = parseDate(d.dtg);
d.temperature = +d.temperature;
//d.hum = +d.hum; // Addon 9 part 3
});
// Scale the range of the data again
x.domain(d3.extent(data, function(d) { return d.dtg; }));
y.domain([0, 60]);
var svg = d3.select("#chart1").transition();
// Make the changes
svg.selectAll(".dot").remove(); //remove old dots
svg.select(".line").duration(750).attr("d", valueline(data));
svg.select("x.axis").duration(750).call(xAxis);
svg.select("y.axis").duration(750).call(yAxis);
//update the scatterplot
svg.selectAll(".dotUpdate")
.data(data)
.attr("class", "dotUpdate")
.enter().append("circle")
.filter(function(d) { return d.temperature > 30 })
.style("fill", "red")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.dtg); })
.attr("cy", function(d) { return y(d.temperature); });
});
}
</script>
The first thing I did wrong was using the wrong d3js.. the following line has to be replaced
<script src="http://d3js.org/d3.v3.min.js"></script>
With the following or else svg.selectAll would not work.
<script type="text/javascript" src="http://d3js.org/d3.v3.js"></script>
Now, as far as the scatter plots update goes. I'm now using the code below which works fine with some databases. In my case it still does not work well and I'll be posting it as a sepearte question as stakoverflow guidlines requsts..
// ** Update data section (Called from the onclick)
function updateData() {
// Get the data again
data = d3.json("2301data.php", function(error, data) {
data.forEach(function(d) {
d.dtg = parseDate(d.dtg);
d.temperature = +d.temperature;
// d.hum = +d.hum; // Addon 9 part 3
});
// Scale the range of the data again
x.domain(d3.extent(data, function(d) { return d.dtg; }));
y.domain([0, 60]); // Addon 9 part 4
var svg = d3.select("#chart1")
var circle = svg.selectAll("circle").data(data)
svg.select(".x.axis") // change the x axis
.transition()
.duration(750)
.call(xAxis);
svg.select(".y.axis") // change the y axis
.transition()
.duration(750)
.call(yAxis);
svg.select(".line") // change the line
.transition()
.duration(750)
.attr("d", valueline(data));
circle.transition()
.duration(750)
.attr("cx", function(d) { return x(d.dtg); })
// enter new circles
circle.enter()
.append("circle")
.filter(function(d) { return d.temperature > 30 })
.style("fill", "red")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.dtg); })
// remove old circles
circle.exit().remove()
});
}

d3: axes being scaled/zoomed, not multi-line graph. How can I fix this issue?

I am plotting dates on the x-axis against cumulative integers on y-axis. I have been trying to implement a feature to be able to scale/zoom into the graph as well as move it. However, I am failing to do this - the axes are changing but the graph is not.
This is my code:
var parseDate = d3.time.format("%d/%m/%Y").parse;
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");//.tickSize(-height);
var yAxis = d3.svg.axis().scale(y)
.orient("left");//.ticks(5);
// Define the line
var cumulativeline = d3.svg.line()
.interpolate("linear")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.cumulative); });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right + 250)
.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);
// Get the data
d3.csv("file.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.cumulative = +d.cumulative;
});
var dataNest = d3.nest()
.key(function(d) {return d.a_tradeidtype;})
.entries(data);
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.cumulative; })]);
// Loop through each a_tradeidtype / key
dataNest.forEach(function(d,i) {
svg.append("path")
.style("stroke", function() { // Add the colours dynamically
return d.color = color(d.key); })
.attr("class", classname)
.attr("d", cumulativeline(d.values))
.attr("clip-path", "url(#clip)");
});
// 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);
// Create zooming component
var zoom = d3.behavior.zoom()
.x(x)
.y(y)
.scaleExtent([1, 10])
.on('zoom', zoomed);
svg.call(zoom);
});
function zoomed() {
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
}
What am I doing wrong?
You're not updating your lines in 'zoomed', just the axes. You need something like this in the function:
svg.selectAll("path")
.attr("d", function(d) { return cumulativeline(d.values); })
;
I also suspect you may need to change the original append routine to something like the below to have data joined to each path so the above code can work:
// Loop through each a_tradeidtype / key
svg.selectAll("path").data(dataNest)
.enter()
.append("path")
.style("stroke", function(d) { // Add the colours dynamically
return d.color = color(d.key); })
.attr("class", classname)
.attr("d", function(d) { return cumulativeline(d.values); })
.attr("clip-path", "url(#clip)")
;
If this doesn't work, stick your code in a jsfiddle

Categories

Resources