Vega-lite: Change how facet creates size to allow for autosize fit with facet

Created on 23 Jul 2019  路  11Comments  路  Source: vega/vega-lite

If faceting by only row, width should be at top level. If faceting by only column, height should be at top level. This would allow for a fit on a facet spec like so, rather than what vega-lite currently produces.

Area - View Composition Bug Enhancement

Most helpful comment

Yep, will prioritize this. Thanks @grauscher!!!

All 11 comments

As discussed on slack, this sounds good. Though we need to make sure that this doesn't lead to a regression somewhere else.

(For example, while we haven't fixed nested facet yet, would this approach work with nested facet in the longer term?)

I don't see a problem if we make sure that there is no faceting into columns at any level.

I'm not sure I understand what you mean by "faceting into columns". If I understand correctly, you just want to make sure this only applies when the row _xor_ column encoding are used directly, and not when they use the facet channel?

What I mean is that if you want to fit on x, then you have to ensure that no view (no matter how deeply nested) facets horizontally.

Would be nice to have a proper way for autosize to work with facets. Use case - embedding Vega Chart into responsive web page. The page don't know the spec - if it's single plot or faceted, it can only provide the width that vega chart should fit.

@alexeypetrushin yep. this issue outlines how the fix would work. you can go ahead and take it on, since this has fallen in priority for my use case.

I agree with @alexeypetrushin , having autosize: fit work with facets in vega-lite is, in my case at least, the missing functionality!

Well, I found a workaround that seems to work fine (but involves changing [just a little bit] the compiled vega code):

  1. First I created the graphic using vega-lite, using the default autosize setting (graphic in vega-lite)
  2. Then I looked at the vega code (graphic in vega)
  3. And just added/changed the following lines in the vega chart:
"autosize": {"type": "fit", "contains": "padding"},
"width": 500,
"height": 300,
"padding": 5,
"signals": [
    {"name": "nrows", "update": "length(data('row_domain'))"},
    {"name": "ncols", "update": "length(data('column_domain'))"},
    {"name": "child_width", "update": "width / ncols"},
    {"name": "child_height", "update": "height / nrows"}
],

And the vega chart seems to work fine, with the defined width and height as the outer values of the container with the canvas!

However, depending on the height and width values chosen, the chart "breaks", since the respective height and width signals are re-calculated when autosize: fit:

As a result, the value of the width and height signals may be changed to modify the layout
[[source](https://vega.github.io/vega/docs/specification/)]

Thanks @willium for the examples in your starting post. They helped me a lot.

I don't really know how the code works to write a good piece of code (and I've never used TypeScript before, actually), but it seems that adding the following lines to vega-lite/src/compile/layoutsize/assemble.ts make it work (at least for the conditions I tested):

     const defaultValue = getViewConfigContinuousSize(model.config.view, isWidth ? 'width' : 'height');
     const safeExpr = `isFinite(${expr}) ? ${expr} : ${defaultValue}`;
     return [{name, init: safeExpr, on: [{update: safeExpr, events: 'window:resize'}]}];
+  } else if (model.parent.component.layoutHeaders.facet.title !== undefined) {
+    return [
+      {
+        name,
+        //value: size
+       update: sizeType === 'width' ? `width / length(data('facet_domain_column'))` : `height / length(data('facet_domain_row'))`
+      }
+    ];
+  } else if ((model.parent.component.layoutHeaders.column.title !== undefined) && (model.parent.component.layoutHeaders.row.title !== undefined)) {
+    return [
+      {
+        name,
+        //value: size
+       update: sizeType === 'width' ? `width / length(data('column_domain'))` : `height / length(data('row_domain'))`
+      }
+    ];
+  } else if (model.parent.component.layoutHeaders.column.title !== undefined) {
+    return [
+      {
+        name,
+        //value: size
+       update: sizeType === 'width' ? `width / length(data('column_domain'))` : `height`
+      }
+    ];
+  } else if (model.parent.component.layoutHeaders.row.title !== undefined) {
+    return [
+      {
+        name,
+        //value: size
+       update: sizeType === 'width' ? `width` : `height / length(data('row_domain'))`
+      }
+    ];
   } else {
     return [
       {

@willium do you want to integrate a patch?

Yep, will prioritize this. Thanks @grauscher!!!

I have been attempting to get this done. The above code works to add the correct signals to the vega spec, but I can't quite figure out how to add width/height to the facet model so it gets passed to vega. I'm also a little iffy on how exact functionality of facet sizing. Here is the assumption I'm working under:

1) If no autosize is on, the width/height is the size of each faceted child (current functionality.)
2) If autosize is on then the width/height is that of the entire combined view

I'm happy to keep on working on this as I need it for a project. I have been learning a ton by stepping through in a debugger, but if someone could point me in the right direction for facetedLayout control that would be awesome.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kanitw picture kanitw  路  4Comments

mcnuttandrew picture mcnuttandrew  路  3Comments

kanitw picture kanitw  路  3Comments

ijlyttle picture ijlyttle  路  3Comments

swanderz picture swanderz  路  4Comments