D3 Time based scales overflowing axis - d3.js

I've been working on a similar stacked bar chart to that in the example here https://bl.ocks.org/mbostock/1134768 however running d3 v4 as shown in the fiddle here https://jsfiddle.net/z75L7cfz/7/
However as you can see in the fiddle, I have a problem that the last element in the graph extends off the end of the axis. I mean I guess this makes sense. But I'm not sure how to make this show in the graph and ensure all the elements fit into the supplied width of the graph.
I assume I need to modify the domain of the x-axis which is currently
x.domain(d3.extent(data, function(d) { return d.Date; }));
But other than somehow manually adding an extra data point with no data in but the subsequent date I'm not sure how to manage this. If anyone could supply any pointers it would be hugely appreciated!

For bar charts, it is best to use band scale. It calculates the bar sizes, location and padding.
var x = d3.scaleBand()
.range([0, width])
.padding(0.05);
Here is the updated fiddle:
https://jsfiddle.net/dtb02eze/2/

Actually, there is nothing wrong with the code per se. The problem here is the very concept of a time scale, and the use of a bar chart with such scale.
If you look closely in your fiddle, each bar starts exactly where it should start, that is, the x value of each bar corresponds to the temporal value in the x axis. But a bar has a width, and here lies the problem: when you set the width to 18px (or any other value), the bar will overflow the time scale. That's normal and expected.
A time scale should be used with dots, where every dot (adimentional) sits exactly in the correct position of the time scale, or with a line, which is simply a path connecting the dots.
Having said all that, you have two options:
Use an ordinal scale. A bar chart is not supposed to use a time scale, and a time scale is not supposed to be used with a bar chart. Bostock's code, for instance, uses an ordinal scale:
var x = d3.scale.ordinal()
You can use scaleOrdinal or, better yet, scaleBand.
If you want to stick to the time scale, change your chart to a line chart. Technically speaking, a line chart is the best option to visualize a trend in data over intervals of time.

Related

Map not rendering, but data is correct

I am a newbie, attempting to copy someone else's choropleth code and use my data.
My data matches their data structure and I can get the correct data to console.log() and my tooltips display correct data.
But my map is just a big block of colour with no paths.
Here is a codepen, but this is the code that renders the map features:
var path = d3.geo.path();
svg.append("g")
.attr("class", "county")
.selectAll("path")
.data(topojson.feature(us, us.objects.counties).features)
.enter().append("path")
.attr("d", path)
.style("fill", function (d) {
return color(pairFipsWithId[d.id]);
})
The result looks like this:
⚠️This question and answer both use d3v3 - d3v4+ geo paths are slightly different, see this question/answer for d3v4+.
If you are making a D3 choropleth, you should be looking for a recent version of D3 - d3v3 is a bit dated now. However, as v3 has some differences, this isn't quite a duplicate of this question, but the problem is the same:
The example map you use has unprojected geographic data
Your geographic data is projected.
In other words, your example's geographic data uses a 3 dimensional coordinate system measured in degrees latitude and longitude, while your geographic data uses a coordinate system where the units are pixels. But, you don't specify this change and D3 does not know to correct for it.
In D3v3, the default projection for a d3.geo.path() is d3.geo.albersUsa(), all D3 projections assume your data is recorded in latitudes and longitudes, otherwise we would need to specify an additional parameter to projections to indicate what coordinate space the input data uses.
A sign that your data is already projected can come from mapshaper.org, if you drag your topojson into the window your map is upside down. Why? Geographic coordinates increase as one moves north (typically up) while pixel coordinates increase as one moves down. This is your data in mapshaper.org:
We cannot "unproject" the data as we don't know what projection was used to create it. But we could assign a null projection to the geo.path:
var path = d3.geo.path().projection(null)
The null projection simply takes each coordinate in the data and converts it to a pixel coordinate with no transform - input values are treated as pixel values. As this map was designed for a web map it doesn't need any scaling or centering (depending on the size of your SVG).
Here's an updated codepen.
In d3v4+, the default projection of a geoPath is a null projection off the bat, it also comes with selection.raise() and selection.lower() methods that can simplify your code a bit, as well as better options for fitting any dataset to a SVG/Canvas of a given size. Here's a d3v4 codePen with a few modifications, d3v5 requires a bit more in updating d3.json

c3.js timeseries bar chart zoom in

I have a timeseries graph done using c3.js. When I zoom the graph the bars do not change the width and the x axis does not adopt accordingly. Is there anyway I can do that? I have tried making axis.x.tick.fit = false. The zoom in works perfectly then. But the groups of data in the graphs are getting overlapped. Any idea on what to do is welcome. Thankyou

can you set .mouseZoomable(true) and .brush(true) for ordinal dc.js barChart?

I am trying add a zoom in and out and brush functions on a dc.js barChart ,but I could not get it to work.
Can you zoom or brush on an ordinal barChart?
No, currently you can't zoom an ordinal chart. The discussion is here:
https://github.com/dc-js/dc.js/issues/416
It isn't entirely clear what it would do, since ordinal scales don't have a continuous range like quantitative scales. I guess the best approach would be to treat it as a sorted list and quantize the zoom so that an exact number of bars are shown.

Responsive d3 bubble chart

I'm trying to implement a responsive d3 bubble chart that updates its container svg (and the nodes it contains) while the page is being resized (kinda like this shows: http://jsfiddle.net/63K4n/)
But instead of just shrinking the nodes down to a size that fits the screen, I want to increase the height of the container as the width is being decreased to create the additional space necessary for the nodes to fall into without being shrunk.
I played with updating the last few lines of the fiddle to this...
chart.attr("width", targetWidth);
chart.attr("height", Math.round(921600/targetWidth)); //was targetWidth/aspectRatio
(where 921,600 was simply the diameter [960] squared - i.e. the total required space)
But of course although this increases the height of the container correctly, the nodes don't exploit the new space - that's what I'm struggling to figure out - how to change the shape of the bubble chart to fit the new space (if that's even possible!)
If not possible with a standard bubble chart, I'd also be happy to try a static force layout (e.g. http://bl.ocks.org/mbostock/1667139) combined with a resizable example like http://bl.ocks.org/mbostock/3355967. Again the problem is finding a way to reshape the nodes to within the confines of their new container, so I'm guessing I'd have to adapt collision detection to account for the svg container boundaries. And with all that going on I'm pretty sure there'd be a decent lag on a mobile device while you're waiting for the force layout to tick its way to something I can update the page with.
Sorry if that's all a little vague, but I'm really just hoping someone has a general suggestion on an appropriate way to tackle the problem. Huge thanks for any thoughts at all!
When a resize event occurs call a function with:
d3.selectAll('#chart svg').remove()
to clear the current chart then pass the new height/size info into a update function that redraws the chart. Its kinda hard to give you a more specific answer without your code and where you're stuck...but that's generally how I redraw my D3 stuff when data or other groovy things change.
Ok, based on the two fiddles/code pens it looks like
var diameter = 500, // <---Feed new variable here
format = d3.format(",d"),
color = d3.scale.category20c();
var width = 450, height = 2000;
var bubble = d3.layout.pack()
.sort(null)
.size([diameter, diameter])
.padding(1.5);
var svg = d3.select("body").append("svg")
.attr("viewBox","0 0 500 500") //<----and Here
.attr("width", diameter)
.attr("height", diameter)
.attr("class", "bubble");
Changing the diameter and the veiwBox attribute makes everything shrink in the proper proportion. Sorry if i miss understood the question, and if I'm still not getting it. But a redraw function that feeds in new data there based on a change in window should work.
Also, just changing the
var height = XXX
doesn't look like its changing the height, since the
.attr('width',diameter)
is using diameter instead of height.
Anyway, I hope that helps.

D3.js zooming and panning - lock on y axis

I'm trying to build a stock chart with zooming functionality using D3.js
I'm looking to start with this example here and attempt to make the zoom feel more natural for a stock chart. A perfect example is this. So the difference as far as I understand is that zoomng and panning are both locked on the Y-axis, and the only way the Y-axis moves is to autmatically fill the price range of the currently visible data.
Another noticeable difference is that zooming does not zoom into the current position of the mouse like it does in the first example.
How can the example be adjusted to work more closely as the other chart? What is the pertitent code, how should it be changed?
Setting the zoom behaviour to not affect the y-axis is simple: just don't attach your y-scale to the zoom behaviour.
In the sample code you linked to, the zoom functionality is added in this line:
this.plot.call(d3.behavior.zoom()
.x(this.x)
.y(this.y)
.on("zoom", this.redraw() )
);
That creates a zoom behaviour object/function, links it to the graphs x and y scales, and tells it to call the function returned by this.redraw() after every zoom event. The zoom behaviour automatically changes the domain of the scales on zoom, and then the redraw function uses the modified zoom. If you don't give it a y scale to modify, then zooming won't affect the y domain.
Getting the y scale to automatically adjust to the given extent of the data is a little trickier. However, remember that the zoom behaviour will have automatically adjusted the domain of the x scale to represent the extent of visible data horizontally. You'll then have to get the corresponding slice of your data array and figure out the extent of y values from it, set your y domain accordingly, and then call the redraw function (remembering that this.redraw() just returns the redraw function, to call it within another function you'll need to use this.redraw()() ).
To have the zoom be independent of the mouse position, set a center point for the zoom behaviour.

Resources