Unable to appropriately assign a size to NVD3 chart - nvd3.js

I am experimenting with an NVD3 chart, and though it renders correctly in shiny dashboard, the div which contains the chart overflows shiny dashboards box() container (does not fit snugly into the box). Explicitly setting height and width for the chart changes the charts size but not the containing div, which continues to overflow the box container; I seemingly have no control over the divs size? Code is as below:
app.R
library(shiny)
library(shinydashboard)
library(rCharts)
library(curl)
consent <- read.csv(curl("https://raw.githubusercontent.com/kilimba/data/master/consent.csv"))
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dashboardBody(
fluidRow(
box(showOutput("distPlot2"),width = 6)
)
)
)
server <- function(input, output) {
output$distPlot2 <- renderChart2({
p2 <- nPlot(HIVConsentRate ~ Year,
group = 'HIVRefused',
data = consent,
type = 'stackedAreaChart',
height = 250,
width = 450)
return(p2)
})
}
shinyApp(ui, server)
Any help appreciated,
Tumaini

I tested your code and added the library argument nvd3 to the UI section like this: box(showOutput("distPlot2",'nvd3'),width = 6) to load the javascript library. I was able to adjust the width of the box on the ui side and/or the width of the chart on the server side.

Sorry guys, still not sure what was the matter, but the tried and tested "switch it off and switch it back on again" worked for me here. Note that I had also forgotten to add the lib variable in showOutput() (at least in this code pasted here, though I had tested with the lib variable set to "nvd3" privately and it had stil proved problematic as far as sizing was concerned. however it works now :)

Related

Tracking d3.zoom transform across page refresh

I'm having a problem with tracking Transforms across page loads, any help much appreciated.
'workspaceDiv' is a full page outer div
'squaregroup' is a g that contains all page elements and can be moved around
For this example I've added a single circle to the squaregroup
workspaceDiv = d3.select("#workspaceDiv")
squaregroup = workspaceDiv.append("g")
.attr("id", "squaregroup")
squaregroup.append("circle").attr("cx", 20).attr("cy", 20).attr("r", 10);
To allow the user to move the g around the page I've attached a d3.zoom.
workspaceDiv.call(zoom);
var zoom = d3.zoom()
.on("zoom", zoomed)
function zoomed(){
squaregroup.attr("transform", d3.event.transform)
}
You might have noticed that I want to transform squaregroup but I have attached the d3.zoom to the workspaceDiv. This is so you can transform it by clicking anywhere on the page (and not only by clicking in the small squaregroup).
On initial page load, this works perfect. Any transforms are also saved as a string in the URL successfully.
On a page reload, the transform is taken from the URL and applied to the sqauregroup:
squaregroup.attr("transform", d3.zoomIdentity.translate(url.x,url.y).scale(url.scale))
Chrome devtools showing the custom transform applied after page reload
[2]: https://i.stack.imgur.com/H93Nf.png
The problem
After a page reload, squaregroup is transformed (see image above), but the d3.event.transform of workspaceDiv is reset, meaning the first drag (of 1 pixel), resets transform (to 0,0) and not with the transform I've applied (200,400).
So the 2nd+ drag is fine, but the first drag throws all data off the page meaning you have to drag around until you find it.
Approaches
Attaching ".call(zoom)" on g means the draggable area is too small, and completely changes the behaviour for the user
I can't find a way to force update the tracking of a .event to be in sync after a page reload
I'm not sure if my approach is wrong, or if there is a function of d3.zoom I just can't find. Any input welcomed!
Many Thanks
Here is the solution (see it in a fiddle)
Make sure you are using zoomIdentity according to the D3 V5 and higher (the sample you tried to use is probably done for the previous versions):
const svg = d3.select('svg');
const group = svg.append('g');
group.append('circle').attr('r', 20);
const zoom = d3.zoom();
const onZoom = () => group.attr('transform', d3.event.transform);
zoom.on("zoom", onZoom);
svg.call(zoom);
const transform = d3.zoomIdentity;
transform.x = 100;
transform.y = 50;
group.call(zoom.transform, transform);

How to apply brushing on a dynamically growing dataset?

I have a dynamically growing timeseries I need to display in a zoomable/panable chart.
Try it out here (in fact: my first jsFiddle ever :) ) :
https://jsfiddle.net/Herkules001/L12k5zwx/29/
I tried to do it the same way as described here: https://dc-js.github.io/dc.js/examples/replacing-data.html
However, each time the chart updates, the zoom and filter are lost on the focus chart. (The brush is preserved on the range chart however.)
How can I add data without resetting the views and losing the zoom?
var chart = dc.lineChart("#test");
var zoom = dc.lineChart("#zoom");
//d3.csv("morley.csv", function(error, experiments) {
var experiments = d3.csvParse(d3.select('pre#data').text());
experiments.forEach(function(x) {
x.Speed = +x.Speed;
});
var ndx = crossfilter(experiments),
runDimension = ndx.dimension(function(d) {return +d.Run;}),
speedSumGroup = runDimension.group().reduceSum(function(d) {return d.Speed * d.Run / 1000;});
chart
.width(768)
.height(400)
.x(d3.scaleLinear().domain([6,20]))
.brushOn(false)
.yAxisLabel("This is the Y Axis!")
.dimension(runDimension)
.group(speedSumGroup)
.rangeChart(zoom);
zoom
.width(768)
.height(80)
.x(d3.scaleLinear().domain([6,20]))
.brushOn(true)
.yAxisLabel("")
.dimension(runDimension)
.group(speedSumGroup);
zoom.render();
chart.render();
var run = 21;
setInterval(
() => {
var chartfilter = chart.filters();
var zoomfilter = zoom.filters();
chart.filter(null);
zoom.filter(null);
ndx.add([{Expt: 6, Run: run++, Speed: 100 + 5 * run}]);
chart.x(d3.scaleLinear().domain([6,run]));
zoom.x(d3.scaleLinear().domain([6,run]));
chart.filter([chartfilter]);
zoom.filter([zoomfilter]);
chart.render();
zoom.render();
},
1000);
//});
In this case, if you are just adding data, you don't need to do the complicated clearing and restoring of filters demonstrated in the example you cited.
That part is only necessary because crossfilter.remove() originally would remove the data that matched the current filters. An awkward interface, almost never what you want.
If you're only adding data, you don't have to worry about any of that:
setInterval(
() => {
ndx.add([{Expt: 6, Run: run++, Speed: 5000 + 5 * run}]);
chart.redraw();
zoom.redraw();
},
5000);
Note that you'll get less flicker, and decent animated transitions, by using redraw instead of render. I also added evadeDomainFilter to avoid lines being clipped before the edge of the chart.
Fork of your fiddle
Removing data
If you use the predicate form of crossfilter.remove() you don't have to worry about saving and restoring filters:
ndx.remove(d => d.Run < run-20);
However, this does expose other bugs in dc.js. Seems like elasticY does not work, similar to what's described in this issue. And you get some weird animations.
Here's a demo with remove enabled.
In the end, dc.js has some pretty neat features, and there is usually a way to get it to do what you want, but it sure is quirky. It's a very complicated domain and in my experience you are going to find some of these quirks in any fully featured charting library.
Update: I fixed the replacing data example, that one is just ndx.remove(() => true) now.
zooming issues
As Joerg pointed out in the comments,
when the chart is not zoomed, it would be nice to have it also grow to show new data as it arrives
the X domain was clipped or even reversed if the focus reached outside the original domain of the chart
We can address these issues by adding a preRedraw event handler. That's the ideal place to adjust the domain; for example you can implement elasticX manually if you need to. (As you'll see in a second, we do!)
First, a naive attempt that's easy to understand:
chart.on('preRedraw', () => {
chart.elasticX(!zoom.filters().length);
});
We can turn elasticX on and off based on whether the range chart has an active filter.
This works and it's nice and simple, but why does the chart get so confused when you try to focus on a domain that wasn't in the original chart?
Welp, it records the original domain (source). So that it can restore to that domain if the focus is cleared, and also to stop you from zooming or panning past the edge of the graph.
But notice from the source link above that we have an escape hatch. It records the original domain when the X scale is set. So, instead of setting elasticX, we can calculate the extent of the data, set the domain of the scale, and tell the chart that the scale is new:
chart.on('preRedraw', () => {
if(!zoom.filters().length) {
var xExtent = d3.extent(speedSumGroup.all(), kv => kv.key);
chart.x(chart.x().domain(xExtent));
}
});
New fiddle with zooming issues fixed.
There is still one glitch which Joerg points out: if you are moving the brush while data comes in, the brush handles occasionally will occasionally stray from the ends of the brush. In my experience, these kinds of glitches are pretty common in D3 (and dynamic charting in general), because it's difficult to think about data changing during user interaction. It probably could be fixed inside the library (perhaps an interrupted transition?) but I'm not going to get into that here.

X3DOM Text-Node in 3D Graph rendered as black box after browserupdate

I implemented something based on http://bl.ocks.org/hlvoorhees/5986172#index.html. After the latest browserupdates I realized that the textnodes in my Application as well as the ones in the example are now rendered as black boxes (one week ago they looked just fine).
The label creating code is (from the example above)
var tickLabels = ticks.selectAll("billboard shape text")
.data(function(d) { return [d]; });
var newTickLabels = tickLabels.enter()
.append("billboard")
.attr("axisOfRotation", "0 0 0")
.append("shape")
.call(makeSolid)
newTickLabels.append("text")
.attr("string", scale.tickFormat(10))
.attr("solid", "true")
.append("fontstyle")
.attr("size", tickFontSize)
.attr("family", "SANS")
.attr("justify", "END MIDDLE" );
tickLabels // enter + update
.attr("string", scale.tickFormat(10))
tickLabels.exit().remove();
The X3DOM-Logdiv doesn't contain any errors or warnings. So I don't know if X3DOM is breaking things or D3, but as it's actually a X3DOM-Node I would locate the problem there.. Neither X3Dom nor D3 updated their libraries lately - however I tried out older versions, but still broken text.
Tested on
Chromium Version 45.0.2454.101 Ubuntu
Firefox 42.0
some current safari (can't tell exactly, sorry)
I had a similar issue while i was trying to get smooth text rendering using big font sizes and a transform to scale them down again. I worked arround the black rectangles by using a small font size and the quality attribute and no transform arround it.
For you it could look like like this:
.append("fontstyle")
.attr("size", 0.5)
.attr("quality","10")
.attr("family", "SANS")
.attr("justify", "END MIDDLE" );
The quality attribute is used for oversampling, so you get nice text rendering even if your text nodes are rather small: http://doc.x3dom.org/author/Text/FontStyle.html
I'm not sure if this is a x3dom bug or if this is intended behaviour for text nodes, which are too small.

Setting the size of an SVG icon in a table cell

I'd like to increase the size of an SVG icon that's displayed inside of a PySide/Qt table cell
icon = QtGui.QIcon('icon.svg')
entry = QtGui.QTableWidgetItem()
entry.setIcon(icon)
entry.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
table.setItem(row_index, column_index, entry)
Absolutely nothing I try increases the size of the displayed SVG. It is currently displayed as a very small icon and also seems to ignore the alignment. The QIcon docs say that items can be scaled, so there must be a way.
What am I doing wrong?
You should add table.setIconSize(QSize(w, h) (if you use QTableWidget) and entry.setSizeHint(QSize(w, h), docs here, like so:
table.setIconSize(QSize(50, 50))
icon = QtGui.QIcon('icon.svg')
entry = QtGui.QTableWidgetItem()
entry.setSizeHint(QSize(50, 50))
entry.setIcon(icon)
entry.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
table.setItem(row_index, column_index, entry)
The alignment cannot be changed I'm afraid (maybe is doable customizing QTableWidget class). If alignment is a must maybe you can use QTableWidget.setCellWidget(), see this answer.

BIRT Pie chart can grow property?

I have a pie chart using BIRT. Works fine but the problem is it only shows data that is "fitted" in the chart's size. Is there a "can grow" property equivalent for pie charts? I mean all data shows only if I resize the pie chart into larger one. But if I choose a larger amount of data, it won't fit again. I need the size to be "auto resize" according to how many data to be displayed.
I tried modifying the advanced settings but nothing worked
Format - Overflow: Auto, Scroll and Visible
Push down - set to true
I do not see any other properties related to pie chart formatting. Can anyone point me to right direction? thanks.
nevermind, found it here but I tweaked it a bit. OnRender event of the chart itself type this:
function afterDataSetFilled(series, dataSet, icsc)
{
if( series.getSeriesIdentifier() == "categorySeries" ){
if( dataSet.getValues().length <= 4 ){
icsc.getChartInstance().getBlock().getBounds().setWidth(450);
icsc.getChartInstance().getBlock().getBounds().setHeight(250);
}
if( dataSet.getValues().length > 4 && dataSet.getValues().length < 8 ){
icsc.getChartInstance().getBlock().getBounds().setWidth(450);
icsc.getChartInstance().getBlock().getBounds().setHeight(400);
}
if( dataSet.getValues().length > 8 ){
icsc.getChartInstance().getBlock().getBounds().setWidth(450);
icsc.getChartInstance().getBlock().getBounds().setHeight(600);
}
}
}
"categorySeries" is the title of the "simple series" type that can be found in "Format Chart" tab when chart is double clicked.

Resources