Sub-selection based on function - d3.js

I have a selection of elements that I'm trying to filter down based on a particular style value (I want just the ones with opacity=1). I'm looking at the documentation for selection.filter along with selection.select and selection.selectAll as well but I'm confused about the correct usage with a function argument.
"select" indicates that it selects the first matching element (as expected) but then the example in the filter documentation shows it being used with a function to select the "odd" elements while maintaining the index.
"selectAll" indicates that you can return an array of elements, but that the function argument is invoked one-by-one in the usual way for each element in the original selection. I'm having difficulty imagining a use case for this.
I guess what I'm wondering is whether there are any tutorials or examples around that discuss the correct usage of these functions?
Thanks,
scott

If you want to reduce a selection to a subset of selected elements, use filter. If you want to select descendent elements, use select or selectAll.
Most often, filter is used to filter elements based on data or index. However, you can access the selected element as this within the filter function. Thus, if you have some elements selected, and you want to reduce that selection to only those elements with an opacity of 1, you can say:
var opaque = selection.filter(function() {
return this.style.opacity == 1;
});
To be safe, you might prefer to look at the computed style rather than the element's style properties. This way, if the opacity is inherited from a stylesheet, you'll get the correct value; otherwise, when a style is inherited this.style.opacity will be the empty string.
var opaque = selection.filter(function() {
return window.getComputedStyle(this, null).getPropertyValue("opacity") == 1;
});
Or equivalently, select the node and use selection.style:
var opaque = selection.filter(function() {
return d3.select(this).style("opacity") == 1;
});
You might find it easier if you filter by data or by class, instead of by computed style property. For example, if you set a class on your nodes, you can filter a selection by class instead:
var opaque = selection.filter(".opaque");

Related

Building umlet custom elements

I am trying to do a collaboration diagram in umlet, but there isn't a palette element to do so. So I thought I'd create one. Easier said than done! I am able to define the class element of the diagram, but am unable to define the arrows that define the flow of the collaboration.
I've tried adding a drawline statement to the code window, but it is undefined in umlet custom element implementation.
//Modify the code below to define the element's behavior.
//
//Example: Change the line
// y += printCenter(textline,y);
//to
// y += 2*printCenter(textline,y);
//and observe the element preview.
int y=textHeight();
drawRectangle(0,0,100,35);
drawRectangle(0,150,100,35);
//drawline(10,35,10,115);
for(String textline : textlines) {
y += printCenter(textline,y);
}
The expected result should be two class elements and two arrows denoting the flow of the collaboration.
I've gotten this far, but I am unable to save it to the custom elements palette.
Welllll, my bad! It turns out that you can use the regular palette elements to construct the diagram and then save it to umlet's palettes directory.

dataCount graph filtered by a dimension

I have a list of participants to various events as the data source
eventid,participant_name
42,xavier
42,gordon
11,john
...
by default, dataCount will say they are 3 participants, I need to display the number of events (so 2)
I tried creating a dimension
var event = ndx.dimension(function(d) {
return d.eventid;
})
but can't manage to use it in dataCount
dc.dataCount(".dc-data-count")
//.dimension(ndx) //working, but counts participants
.dimension(event) // not working
How do I do that?
It sounds to me like you are trying to use the data count widget to count group bins rather than rows.
The data count widget is only designed to count records, not keys or groups or anything else. But you could fake out the objects, since the widget is calling just .size() on the dimension, and just .value() on the group.
But what to put there? The value is actually sort of easy, since it's the count of groups with non-zero value:
var eventGroup = event.group();
widget.group({value: function() {
return eventGroup.all().filter(function(kv) { return kv.value>0; }).length;
})
But what is the size? Well, according to the crossfilter documentation, group.size actually returns what we want, "the number of distinct values in the group, independent of any filters; the cardinality."
So oddly, it seems like
widget.dimension(eventGroup)
should work. Of course, I haven't tested any of this, so please comment if this doesn't work!
(Sigh, what I wouldn't do for a real data model in dc.js. It is rather confusing that the "dimension" for this widget is actually the crossfilter object. Another place where there is kind of a weird economy of methods, like the dimension's group, which is just a function.)

Set a max-width and max-height for table cells

Is it possible to set a max-width and max-height for cells in Handsontable?
I've tried to set this via CSS on the <th> and <td> elements but this doesn't work.
I saw in the docs that you can set the columns to particular widths, but not maximums.
So, the solution is to use your HOT instance's manualColumnWidths array. This array will set the widths for all columns automatically. It's nested somewhere in the code so that anytime there's a change to the DOM it updates which is why using CSS or jQuery on the widths of the elements themselves wasn't working. It's a good structure, we just need this to be more explicitly written somewhere.
Note that you can repeat this for rows by just using manualRowHeights instead, as well as the rest of the row methods.
Now, here is how you would set maximums and minimums for column resizing events. Set the afterColumnResize option to this following function:
function setAfterColumnResize() {
return function(col, size) {
var headers = $(".colHeader"); // first is empty if there are rowHeaders
var maxW = [10, 200, 400]; // note that the first is the row header
var minW = [1, 100, 100]; // note that the first is the row header
var actualCol = col + 1; // this is to account for the row header
var maxColW = maxW[actualCol];
var minColW = minW[actualCol];
var actualW = $(headers[actualCol]).parent().parent().width();
if (hot && maxColW && actualW > maxColW) {
hot.manualColumnWidths[col] = maxColW;
hot.render()
}
if (hot && minColW && actualW < minColW) {
hot.manualColumnWidths[col] = minColW;
hot.render()
}
}
}
Now let me explain it. When this event gets triggered, you first find the header using the col index it gives you. Correct it if you have row headers set to true (which is what I do here and in the fiddle). Then you set the arrays of max/min widths (of course you can do this more elegantly in your code).
Then grab the actual current width of the column in question by looking at the parent's parent of the span (this is a little messy but it's the simplest way I found of doing). The conditional after should be self explanatory.
And lastly, the important bits. You can see that by accessing hot.manualColumnWidths you can set the width that you want. I set it to the max if my current width exceeds it. Then just re-render and you're done!
There are a few issues like the fact that these events get called only after resizing so if you originally set the table to render with a width larger than your max, it won't do anything. If that's the case, just call this function after rendering the table the first time (call it once per column).
This would also be the case if you didn't want to use the column resizing event; in this case, it would just be a normal function that you call after rendering.
Hope this helps! Here is a demo fiddle showing it in action :D

Need pauses - timing not working on iterating through text strings w/ d3/svg (piling up on each other)

I am trying to use d3 to animate text using an svg text with d3 transitions. I have it working as desired for a single string.
I want to iterate through strings from an array of json objects.
I can do this as well.
All the painting and transitions work great. Problem is, they all happen at once, and appear piled up on each other, and all animate all at once.
I have tried putting them in a setTimeout() to get them to appear sequentially.
Still does not work.
for ( i in haikuStr ) {
if( i !=0 ){
//Make it wait if an appropriate time it is not the first one
setTimeout( function() {
showText();
}, 11000 * i );
} else {
//if i=0, don't make folks wait
showText();
}
}
The showText() function is the full create container -> finish transitions.
I use 11000 * i to ensure that >2 iterations have 11 additional seconds per i.
I have spent quite a bit of time reading and trying to figure out how to get the loop to pause before cycling through to paint the next line.
Any thoughts or ideas would be appreciated.
The un-timed example is here, if you wish to see the text jumble up:
http://www.mysalmagundi.com/js/svg-d3-no-timing.html
Have you read Thinking with Joins? Or some of the other introductory D3 tutorials, such as those by Scott Murray? Or Three Little Circles, or Working with Selections? I ask because your showText function is misusing data joins; it creates text elements for every element in the global haikuStr array:
var text = haikuContainer.selectAll("text")
.data(haikuStr)
.html(String)
.enter().append("text");
And all your text elements are overlapping because you set them to have the same y-attribute:
var thisHaiku = text
.attr("x", -800)
.attr("y", 120)
(Also, that selection.html call is a no-op because the update selection is guaranteed to be empty, since you just created haikuContainer it is guaranteed to not have any descendant text elements. And thisHaiku is the same value as the var text, because when method chaining selection.attr and similar methods return the current selection; so there’s no reason to create a separate var. Also, you shouldn’t use a for-in loop to iterate over arrays.)
If you wait 11 seconds, you’ll see the second SVG appear, but because of your data join as described above, it has the same overlapping text content.
If you just want to show a single piece of text, then pass that string to your showText function (e.g., showText("hello")). Then, since you’re just creating individual elements, just selection.append them rather than using a data-join. You only need the data-join when you’re creating (or updating or removing) a variable number of elements based on data; in this case it looks like you’re trying to just create a single element.

access current attributes in d3 functions

circle is an array of four d3 circles.
circle
.attr( "cy", function(){ this.attr("cy") + 10*input_data.pitch });
This fails. How can I access the individual attributes in anonymous functions like above?
Inside your function, this is an Element per the W3C DOM API. So it’s just this.getAttribute("cy").
Two more things: you forgot to return a value. And since attribute values are strings, you’ll need to coerce them to number before you can add another number. Otherwise your number will get coerced to a string and then the two strings will be concatenated: "10" + 2 is "102", not 12.
So, like this:
circle.attr("cy", function() {
return +this.getAttribute("cy") + 10 * input_data.pitch;
});
All that said, it’s generally not idiomatic D3 to pull data back out of DOM attributes. (It’s slow and you have coercion and serialization problems since DOM attributes can only be strings.) So I would recommend looking for a way to do this based on data, and limit yourself to data-driven documents… not document-driven data!

Resources