Repository: microsoft/SandDance (language: python)

Issue:
Feature request: Show z-axis in 3d view



Discussion / hints:
Yes, 3D plots are somewhat meaningless without the axis.

Fix patch:
diff --git a/extensions/azdata-sanddance/CHANGELOG.md b/extensions/azdata-sanddance/CHANGELOG.md
index 8b5af8e4a..356779ac2 100644
--- a/extensions/azdata-sanddance/CHANGELOG.md
+++ b/extensions/azdata-sanddance/CHANGELOG.md
@@ -1,5 +1,8 @@
 # Change Log
 
+## [3.3.0]
+- Show the z axis scale in 3d
+
 ## [3.2.0]
 - Fix for last bin of quantitative band scale
 - Support virtual workspaces
diff --git a/extensions/azdata-sanddance/package.json b/extensions/azdata-sanddance/package.json
index 795d154be..992fdc6aa 100644
--- a/extensions/azdata-sanddance/package.json
+++ b/extensions/azdata-sanddance/package.json
@@ -2,7 +2,7 @@
     "name": "azdata-sanddance",
     "displayName": "SandDance for Azure Data Studio",
     "description": "Visually explore, understand, and present your data.",
-    "version": "3.2.0",
+    "version": "3.3.0",
     "icon": "sanddance-logo.png",
     "preview": true,
     "publisher": "msrvida",
diff --git a/extensions/vscode-sanddance/CHANGELOG.md b/extensions/vscode-sanddance/CHANGELOG.md
index 0e021cf88..a281421eb 100644
--- a/extensions/vscode-sanddance/CHANGELOG.md
+++ b/extensions/vscode-sanddance/CHANGELOG.md
@@ -1,5 +1,8 @@
 # Change Log
 
+## [3.3.0]
+- Show the z axis scale in 3d
+
 ## [3.2.0]
 - Fix for last bin of quantitative band scale
 - Support virtual workspaces
diff --git a/extensions/vscode-sanddance/package.json b/extensions/vscode-sanddance/package.json
index 24c7f816d..3e6ed7515 100644
--- a/extensions/vscode-sanddance/package.json
+++ b/extensions/vscode-sanddance/package.json
@@ -2,7 +2,7 @@
     "name": "vscode-sanddance",
     "displayName": "SandDance for VSCode",
     "description": "Visually explore, understand, and present your data.",
-    "version": "3.2.0",
+    "version": "3.3.0",
     "icon": "sanddance-logo.png",
     "publisher": "msrvida",
     "repository": {
diff --git a/packages/powerbi/pbiviz.json b/packages/powerbi/pbiviz.json
index 0a3a40ca5..15027a05b 100644
--- a/packages/powerbi/pbiviz.json
+++ b/packages/powerbi/pbiviz.json
@@ -4,7 +4,7 @@
         "displayName": "SandDance 2019",
         "guid": "SandDance201929976D117A654D0BAB8E96507442D80B",
         "visualClassName": "Visual",
-        "version": "3.2.0",
+        "version": "3.3.0",
         "description": "Visually explore, understand, and present your data.",
         "supportUrl": "https://github.com/Microsoft/SandDance/issues",
         "gitHubUrl": "https://github.com/microsoft/SandDance"
diff --git a/packages/sanddance-specs/package.json b/packages/sanddance-specs/package.json
index 16df7d947..18d6e7619 100644
--- a/packages/sanddance-specs/package.json
+++ b/packages/sanddance-specs/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@msrvida/sanddance-specs",
-  "version": "1.3.0",
+  "version": "1.4.0",
   "description": "SandDance Vega specification generator.",
   "main": "dist/es6/index.js",
   "types": "dist/es6/index.d.ts",
diff --git a/packages/sanddance-specs/src/axes.ts b/packages/sanddance-specs/src/axes.ts
index 352386839..6997067ac 100644
--- a/packages/sanddance-specs/src/axes.ts
+++ b/packages/sanddance-specs/src/axes.ts
@@ -6,7 +6,7 @@ import { GlobalScope } from './globalScope';
 import { AxisScale, AxisScales, GlobalScales } from './interfaces';
 import { addAxes, addScales } from './scope';
 import { SpecColumns, SpecViewOptions } from './types';
-import { Column } from '@msrvida/chart-types';
+import { Column, View } from '@msrvida/chart-types';
 import {
     Axis,
     NewSignal,
@@ -28,16 +28,17 @@ export interface AxesScopeMap {
 }
 
 interface Props {
-    globalScope: GlobalScope,
-    allGlobalScales: GlobalScales[],
-    axisScales: AxisScales,
-    plotOffsetSignals: { x: NewSignal, y: NewSignal },
-    axesOffsets: { x: number, y: number },
-    axesTitlePadding: { x: number, y: number },
-    labelBaseline: { x: TextBaselineValue, y: TextBaselineValue },
-    specColumns: SpecColumns,
-    specViewOptions: SpecViewOptions,
-    axesScopes: AxesScopeMap
+    globalScope: GlobalScope;
+    allGlobalScales: GlobalScales[];
+    axisScales: AxisScales;
+    plotOffsetSignals: { x: NewSignal, y: NewSignal };
+    axesOffsets: { x: number, y: number };
+    axesTitlePadding: { x: number, y: number };
+    labelBaseline: { x: TextBaselineValue, y: TextBaselineValue };
+    specColumns: SpecColumns;
+    specViewOptions: SpecViewOptions;
+    axesScopes: AxesScopeMap;
+    view: View;
 }
 
 export function addGlobalAxes(props: Props) {
@@ -46,16 +47,27 @@ export function addGlobalAxes(props: Props) {
 
     allGlobalScales.forEach(globalScales => {
         const { scales } = globalScales;
-        for (let s in scales) {
-            let _scales: Scale[] = scales[s];
+        for (let xyz in scales) {
+            let _scales: Scale[] = scales[xyz];
             if (_scales) {
                 addScales(scope, ..._scales);
-                if (globalScales.showAxes && axisScales && s !== 'z') {
-                    let axisScale: AxisScale = axisScales[s];
+                let { showAxes } = globalScales;
+                let zindex: number = undefined;
+                if (xyz === 'z') {
+                    showAxes = false;
+                    if (props.view === '3d' && specViewOptions.zAxisOptions) {
+                        if (specViewOptions.zAxisOptions.showZAxis) {
+                            showAxes = true;
+                            zindex = specViewOptions.zAxisOptions.zIndex;
+                        }
+                    }
+                }
+                if (showAxes && axisScales) {
+                    let axisScale: AxisScale = axisScales[xyz];
                     if (axisScale) {
                         const lineColor = specViewOptions.colors.axisLine;
-                        const horizontal = s === 'x';
-                        const column: Column = specColumns[s] || { quantitative: true };
+                        const horizontal = xyz === 'x';
+                        const column: Column = specColumns[xyz] || { quantitative: true };
                         const title = axisScale.title;
                         const props: AxisProps = {
                             title,
@@ -63,8 +75,9 @@ export function addGlobalAxes(props: Props) {
                             column,
                             specViewOptions,
                             lineColor,
-                            titlePadding: axesTitlePadding[s],
-                            labelBaseline: labelBaseline[s]
+                            titlePadding: axesTitlePadding[xyz],
+                            labelBaseline: labelBaseline[xyz],
+                            zindex
                         };
                         axesScopes['main'].forEach(a => addAxes(a.scope, createAxis({
                             ...props,
@@ -74,8 +87,8 @@ export function addGlobalAxes(props: Props) {
                             showLines: a.lines
                         })));
 
-                        if (axesScopes[s]) {
-                            axesScopes[s].forEach(a => addAxes(a.scope, createAxis({
+                        if (axesScopes[xyz]) {
+                            axesScopes[xyz].forEach(a => addAxes(a.scope, createAxis({
                                 ...props,
                                 scale: a.scale || _scales[0],
                                 showTitle: a.title,
@@ -84,9 +97,9 @@ export function addGlobalAxes(props: Props) {
                             })));
                         }
 
-                        if (plotOffsetSignals[s] && axesOffsets[s]) {
-                            const plotOffsetSignal = plotOffsetSignals[s] as NewSignal;
-                            plotOffsetSignal.update = `${axesOffsets[s]}`;
+                        if (plotOffsetSignals[xyz] && axesOffsets[xyz]) {
+                            const plotOffsetSignal = plotOffsetSignals[xyz] as NewSignal;
+                            plotOffsetSignal.update = `${axesOffsets[xyz]}`;
                         }
                     }
                 }
@@ -107,11 +120,13 @@ interface AxisProps {
     showLabels?: boolean;
     titlePadding: number;
     labelBaseline: TextBaselineValue;
+    zindex: number;
 }
 
 function createAxis(props: AxisProps) {
-    const { column, horizontal, labelBaseline, lineColor, scale, showLabels, showTitle, showLines, specViewOptions, title, titlePadding } = props;
+    const { column, horizontal, labelBaseline, lineColor, scale, showLabels, showTitle, showLines, specViewOptions, title, titlePadding, zindex } = props;
     const axis: Axis = {
+        zindex,
         scale: scale.name,
         orient: horizontal ? 'bottom' : 'left',
         domain: showLines,
diff --git a/packages/sanddance-specs/src/build.ts b/packages/sanddance-specs/src/build.ts
index ad346bf56..9c68a78ba 100644
--- a/packages/sanddance-specs/src/build.ts
+++ b/packages/sanddance-specs/src/build.ts
@@ -1,13 +1,14 @@
 // Copyright (c) Microsoft Corporation. All rights reserved.
 // Licensed under the MIT license.
-import { getSpecBuilderForChart } from './charts';
+import { getSpecBuilderPropsForChart } from './charts';
 import { inferAll } from './inference';
 import { SpecContext } from './types';
 import { SpecResult } from './interfaces';
 import { ValuesData } from 'vega-typings';
+import { SpecBuilder } from './specBuilder';
 
-export function build(context: SpecContext, currData: object[]): SpecResult {
-    const { specColumns } = context;
+export function build(specContext: SpecContext, currData: object[]): SpecResult {
+    const { specColumns } = specContext;
     const columns = [
         specColumns.color,
         specColumns.facet,
@@ -21,12 +22,22 @@ export function build(context: SpecContext, currData: object[]): SpecResult {
     ];
     inferAll(columns, currData);
 
-    const specBuilder = getSpecBuilderForChart(context);
+    const specBuilderProps = getSpecBuilderPropsForChart(specContext);
+    const specBuilder = new SpecBuilder(specBuilderProps, specContext);
     let specResult: SpecResult;
 
     if (specBuilder) {
         try {
-            specResult = specBuilder.build();
+            const errors = specBuilder.validate();
+            if (errors.length) {
+                specResult = {
+                    errors,
+                    specCapabilities: specBuilderProps.specCapabilities,
+                    vegaSpec: null
+                };
+            } else {
+                specResult = specBuilder.build();
+            }
         }
         catch (e) {
             specResult = {
@@ -43,7 +54,7 @@ export function build(context: SpecContext, currData: object[]): SpecResult {
         specResult = {
             specCapabilities: null,
             vegaSpec: null,
-            errors: [`could not build spec for ${context.insight.chart}`]
+            errors: [`could not build spec for ${specContext.insight.chart}`]
         };
     }
     return specResult;
diff --git a/packages/sanddance-specs/src/charts/barchartH.ts b/packages/sanddance-specs/src/charts/barchartH.ts
index d84231ff7..a2e679acc 100644
--- a/packages/sanddance-specs/src/charts/barchartH.ts
+++ b/packages/sanddance-specs/src/charts/barchartH.ts
@@ -3,12 +3,12 @@
 import { SignalNames } from '../constants';
 import { defaultBins, maxbins, minBarBandWidth } from '../defaults';
 import { AxisScale, AxisScales } from '../interfaces';
-import { AggregateContainer, AggregateContainerProps } from '../layouts/aggregateContainer';
-import { Band, BandProps } from '../layouts/band';
+import { AggregateContainerProps } from '../layouts/aggregateContainer';
+import { BandProps } from '../layouts/band';
 import { LayoutPair } from '../layouts/layout';
-import { Square, SquareProps } from '../layouts/square';
-import { Strip, StripProps } from '../layouts/strip';
-import { Treemap, TreemapProps } from '../layouts/treemap';
+import { SquareProps } from '../layouts/square';
+import { StripProps } from '../layouts/strip';
+import { TreemapProps } from '../layouts/treemap';
 import { allowNoneForSize } from '../size';
 import { SpecBuilderProps } from '../specBuilder';
 import { SpecContext } from '../types';
@@ -35,7 +35,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
         z: { title: specColumns.z && specColumns.z.name }
     };
     const layouts: LayoutPair[] = [{
-        layoutClass: Band,
+        layoutType: 'Band',
         props: bandProps
     }];
     if (insight.totalStyle === 'sum-strip-percent') {
@@ -50,7 +50,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
             z: specColumns.z
         };
         layouts.push({
-            layoutClass: Strip,
+            layoutType: 'Strip',
             props: stripProps
         });
     } else {
@@ -63,7 +63,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
             showAxes: true
         };
         layouts.push({
-            layoutClass: AggregateContainer,
+            layoutType: 'AggregateContainer',
             props: aggProps
         });
         switch (insight.totalStyle) {
@@ -77,7 +77,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                     z: specColumns.z
                 };
                 layouts.push({
-                    layoutClass: Treemap,
+                    layoutType: 'Treemap',
                     props: treemapProps
                 });
                 break;
@@ -93,7 +93,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                     z: specColumns.z
                 };
                 layouts.push({
-                    layoutClass: Strip,
+                    layoutType: 'Strip',
                     props: stripProps
                 });
                 break;
@@ -109,7 +109,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                     z: specColumns.z
                 };
                 layouts.push({
-                    layoutClass: Strip,
+                    layoutType: 'Strip',
                     props: stripProps
                 });
                 break;
@@ -125,7 +125,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                     maxGroupedFillSize: aggProps.globalAggregateMaxExtentScaledSignal
                 };
                 layouts.push({
-                    layoutClass: Square,
+                    layoutType: 'Square',
                     props: squareProps
                 });
                 break;
@@ -147,6 +147,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                 },
                 {
                     role: 'z',
+                    axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
                     allowNone: true
                 },
                 {
diff --git a/packages/sanddance-specs/src/charts/barchartV.ts b/packages/sanddance-specs/src/charts/barchartV.ts
index f3844ec57..237a921ea 100644
--- a/packages/sanddance-specs/src/charts/barchartV.ts
+++ b/packages/sanddance-specs/src/charts/barchartV.ts
@@ -3,12 +3,12 @@
 import { SignalNames } from '../constants';
 import { defaultBins, maxbins, minBarBandWidth } from '../defaults';
 import { AxisScale, AxisScales } from '../interfaces';
-import { AggregateContainer, AggregateContainerProps } from '../layouts/aggregateContainer';
-import { Band, BandProps } from '../layouts/band';
+import { AggregateContainerProps } from '../layouts/aggregateContainer';
+import { BandProps } from '../layouts/band';
 import { LayoutPair } from '../layouts/layout';
-import { Square, SquareProps } from '../layouts/square';
-import { Strip, StripProps } from '../layouts/strip';
-import { Treemap, TreemapProps } from '../layouts/treemap';
+import { SquareProps } from '../layouts/square';
+import { StripProps } from '../layouts/strip';
+import { TreemapProps } from '../layouts/treemap';
 import { allowNoneForSize } from '../size';
 import { SpecBuilderProps } from '../specBuilder';
 import { SpecContext } from '../types';
@@ -35,7 +35,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
         z: { title: specColumns.z && specColumns.z.name }
     };
     const layouts: LayoutPair[] = [{
-        layoutClass: Band,
+        layoutType: 'Band',
         props: bandProps
     }];
     if (insight.totalStyle === 'sum-strip-percent') {
@@ -50,7 +50,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
             z: specColumns.z
         };
         layouts.push({
-            layoutClass: Strip,
+            layoutType: 'Strip',
             props: stripProps
         });
     } else {
@@ -63,7 +63,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
             showAxes: true
         };
         layouts.push({
-            layoutClass: AggregateContainer,
+            layoutType: 'AggregateContainer',
             props: aggProps
         });
         switch (insight.totalStyle) {
@@ -77,7 +77,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                     z: specColumns.z
                 };
                 layouts.push({
-                    layoutClass: Treemap,
+                    layoutType: 'Treemap',
                     props: treemapProps
                 });
                 break;
@@ -93,7 +93,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                     z: specColumns.z
                 };
                 layouts.push({
-                    layoutClass: Strip,
+                    layoutType: 'Strip',
                     props: stripProps
                 });
                 break;
@@ -108,7 +108,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                     z: specColumns.z
                 };
                 layouts.push({
-                    layoutClass: Strip,
+                    layoutType: 'Strip',
                     props: stripProps
                 });
                 break;
@@ -124,7 +124,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                     maxGroupedFillSize: aggProps.globalAggregateMaxExtentScaledSignal
                 };
                 layouts.push({
-                    layoutClass: Square,
+                    layoutType: 'Square',
                     props: squareProps
                 });
                 break;
@@ -146,6 +146,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                 },
                 {
                     role: 'z',
+                    axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
                     allowNone: true
                 },
                 {
diff --git a/packages/sanddance-specs/src/charts/density.ts b/packages/sanddance-specs/src/charts/density.ts
index 175ae3d51..89914f43f 100644
--- a/packages/sanddance-specs/src/charts/density.ts
+++ b/packages/sanddance-specs/src/charts/density.ts
@@ -3,12 +3,12 @@
 import { SignalNames } from '../constants';
 import { defaultBins, maxbins, minBarBandWidth } from '../defaults';
 import { AxisScales } from '../interfaces';
-import { AggregateSquare, AggregateSquareProps } from '../layouts/aggregateSquare';
-import { Band, BandProps } from '../layouts/band';
+import { AggregateSquareProps } from '../layouts/aggregateSquare';
+import { BandProps } from '../layouts/band';
 import { LayoutPair } from '../layouts/layout';
-import { Square, SquareProps } from '../layouts/square';
-import { Strip, StripProps } from '../layouts/strip';
-import { Treemap, TreemapProps } from '../layouts/treemap';
+import { SquareProps } from '../layouts/square';
+import { StripProps } from '../layouts/strip';
+import { TreemapProps } from '../layouts/treemap';
 import { allowNoneForSize } from '../size';
 import { SpecBuilderProps } from '../specBuilder';
 import { SpecContext } from '../types';
@@ -53,15 +53,15 @@ export default function (specContext: SpecContext): SpecBuilderProps {
     };
     const layouts: LayoutPair[] = [
         {
-            layoutClass: Band,
+            layoutType: 'Band',
             props: vBandProps
         },
         {
-            layoutClass: Band,
+            layoutType: 'Band',
             props: hBandProps
         },
         {
-            layoutClass: AggregateSquare,
+            layoutType: 'AggregateSquare',
             props: aggProps
         }
     ];
@@ -75,7 +75,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                 z: specColumns.z
             };
             layouts.push({
-                layoutClass: Treemap,
+                layoutType: 'Treemap',
                 props: treemapProps
             });
             break;
@@ -90,7 +90,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                 z: specColumns.z
             };
             layouts.push({
-                layoutClass: Strip,
+                layoutType: 'Strip',
                 props: stripProps
             });
             break;
@@ -104,7 +104,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                 z: specColumns.z
             };
             layouts.push({
-                layoutClass: Strip,
+                layoutType: 'Strip',
                 props: stripProps
             });
             break;
@@ -123,7 +123,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                 squareProps.maxGroupedFillSize = aggMaxExtentScaled;
             };
             layouts.push({
-                layoutClass: Square,
+                layoutType: 'Square',
                 props: squareProps
             });
             break;
@@ -149,6 +149,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                 },
                 {
                     role: 'z',
+                    axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
                     allowNone: true
                 },
                 {
diff --git a/packages/sanddance-specs/src/charts/grid.ts b/packages/sanddance-specs/src/charts/grid.ts
index 88f3aa2d7..7416ea27d 100644
--- a/packages/sanddance-specs/src/charts/grid.ts
+++ b/packages/sanddance-specs/src/charts/grid.ts
@@ -4,7 +4,7 @@ import { AxisScales } from '../interfaces';
 import { SignalNames } from '../constants';
 import { SpecBuilderProps } from '../specBuilder';
 import { SpecContext } from '../types';
-import { Square, SquareProps } from '../layouts/square';
+import { SquareProps } from '../layouts/square';
 
 export default function (specContext: SpecContext): SpecBuilderProps {
     const { specColumns } = specContext;
@@ -21,7 +21,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
         axisScales,
         layouts: [
             {
-                layoutClass: Square,
+                layoutType: 'Square',
                 props: squareProps
             }
         ],
@@ -30,6 +30,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
             roles: [
                 {
                     role: 'z',
+                    axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
                     allowNone: true
                 },
                 {
diff --git a/packages/sanddance-specs/src/charts/index.ts b/packages/sanddance-specs/src/charts/index.ts
index a81aa2f31..383d2aedd 100644
--- a/packages/sanddance-specs/src/charts/index.ts
+++ b/packages/sanddance-specs/src/charts/index.ts
@@ -8,8 +8,12 @@ import scatterplot from './scatterplot';
 import stacks from './stacks';
 import strips from './strips';
 import treemap from './treemap';
-import { SpecBuilder, SpecBuilderProps } from '../specBuilder';
+import { SpecBuilderProps } from '../specBuilder';
 import { SpecContext } from '../types';
+import { getFacetLayout } from '../facetLayout';
+import { DiscreteColumn } from '../interfaces';
+import { SignalNames } from '../constants';
+import { defaultBins, maxbins } from '../defaults';
 
 const map: { [chart: string]: (specContext: SpecContext) => SpecBuilderProps } = {
     barchart: barchartV,
@@ -23,13 +27,30 @@ const map: { [chart: string]: (specContext: SpecContext) => SpecBuilderProps } =
     treemap
 };
 
-export function getSpecBuilderForChart(specContext: SpecContext) {
-    const { insight } = specContext;
-    let props: SpecBuilderProps;
-
+export function getSpecBuilderPropsForChart(specContext: SpecContext) {
+    const { insight, specColumns, specViewOptions } = specContext;
     const fn = map[insight.chart];
     if (fn) {
-        props = fn(specContext);
-        return new SpecBuilder({ ...props, specContext });
+        const props = fn(specContext);
+        if (insight.columns.facet) {
+            const discreteFacetColumn: DiscreteColumn = {
+                column: specColumns.facet,
+                defaultBins,
+                maxbins,
+                maxbinsSignalDisplayName: specViewOptions.language.FacetMaxBins,
+                maxbinsSignalName: SignalNames.FacetBins
+            };
+            const discreteFacetVColumn: DiscreteColumn = {
+                column: specColumns.facetV,
+                defaultBins,
+                maxbins,
+                maxbinsSignalDisplayName: specViewOptions.language.FacetVMaxBins,
+                maxbinsSignalName: SignalNames.FacetVBins
+            };
+            const { facetLayout, layoutPair } = getFacetLayout(insight.facetStyle, discreteFacetColumn, discreteFacetVColumn, specViewOptions.colors.axisText);
+            props.layouts.unshift(layoutPair);
+            props.facetLayout = facetLayout;
+        }
+        return props;
     }
 }
diff --git a/packages/sanddance-specs/src/charts/scatterplot.ts b/packages/sanddance-specs/src/charts/scatterplot.ts
index e9f6aeb99..2518b2b32 100644
--- a/packages/sanddance-specs/src/charts/scatterplot.ts
+++ b/packages/sanddance-specs/src/charts/scatterplot.ts
@@ -1,7 +1,7 @@
 // Copyright (c) Microsoft Corporation. All rights reserved.
 // Licensed under the MIT license.
 import { AxisScales } from '../interfaces';
-import { Scatter, ScatterProps } from '../layouts/scatter';
+import { ScatterProps } from '../layouts/scatter';
 import { SignalNames } from '../constants';
 import { SpecBuilderProps } from '../specBuilder';
 import { SpecContext } from '../types';
@@ -25,7 +25,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
         axisScales,
         layouts: [
             {
-                layoutClass: Scatter,
+                layoutType: 'Scatter',
                 props: scatterProps
             }
         ],
@@ -42,6 +42,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                 },
                 {
                     role: 'z',
+                    axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
                     allowNone: true
                 },
                 {
diff --git a/packages/sanddance-specs/src/charts/stacks.ts b/packages/sanddance-specs/src/charts/stacks.ts
index 53ac5776c..57dfd858e 100644
--- a/packages/sanddance-specs/src/charts/stacks.ts
+++ b/packages/sanddance-specs/src/charts/stacks.ts
@@ -1,18 +1,19 @@
 // Copyright (c) Microsoft Corporation. All rights reserved.
 // Licensed under the MIT license.
 import { AxisScales } from '../interfaces';
-import { Band, BandProps } from '../layouts/band';
+import { BandProps } from '../layouts/band';
 import { defaultBins, maxbins, minBarBandWidth } from '../defaults';
 import { SignalNames } from '../constants';
 import { SpecBuilderProps } from '../specBuilder';
 import { SpecContext } from '../types';
-import { Stack, StackProps } from '../layouts/stack';
+import { StackProps } from '../layouts/stack';
 
 export default function (specContext: SpecContext): SpecBuilderProps {
-    const { specColumns } = specContext;
+    const { specColumns, specViewOptions } = specContext;
     const axisScales: AxisScales = {
         x: { title: specColumns.x && specColumns.x.name },
-        y: { title: specColumns.y && specColumns.y.name }
+        y: { title: specColumns.y && specColumns.y.name },
+        z: { title: specViewOptions.language.count }
     };
     const hBandProps: BandProps = {
         excludeEncodingRuleMap: true,
@@ -48,15 +49,15 @@ export default function (specContext: SpecContext): SpecBuilderProps {
         customZScale: true,
         layouts: [
             {
-                layoutClass: Band,
+                layoutType: 'Band',
                 props: vBandProps
             },
             {
-                layoutClass: Band,
+                layoutType: 'Band',
                 props: hBandProps
             },
             {
-                layoutClass: Stack,
+                layoutType: 'Stack',
                 props: stackProps
             }
         ],
diff --git a/packages/sanddance-specs/src/charts/strips.ts b/packages/sanddance-specs/src/charts/strips.ts
index c9d1e2960..bae022d7d 100644
--- a/packages/sanddance-specs/src/charts/strips.ts
+++ b/packages/sanddance-specs/src/charts/strips.ts
@@ -1,12 +1,12 @@
 // Copyright (c) Microsoft Corporation. All rights reserved.
 // Licensed under the MIT license.
-import { AggregateContainer, AggregateContainerProps } from '../layouts/aggregateContainer';
+import { AggregateContainerProps } from '../layouts/aggregateContainer';
 import { AxisScales } from '../interfaces';
 import { LayoutPair } from '../layouts/layout';
 import { SignalNames } from '../constants';
 import { SpecBuilderProps } from '../specBuilder';
 import { SpecContext } from '../types';
-import { Strip, StripProps } from '../layouts/strip';
+import { StripProps } from '../layouts/strip';
 
 export default function (specContext: SpecContext): SpecBuilderProps {
     const { specColumns } = specContext;
@@ -37,12 +37,12 @@ export default function (specContext: SpecContext): SpecBuilderProps {
             showAxes: false
         };
         layouts.push({
-            layoutClass: AggregateContainer,
+            layoutType: 'AggregateContainer',
             props
         });
     }
     layouts.push({
-        layoutClass: Strip,
+        layoutType: 'Strip',
         props: stripProps
     });
     return {
@@ -58,6 +58,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                 },
                 {
                     role: 'z',
+                    axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
                     allowNone: true
                 },
                 {
diff --git a/packages/sanddance-specs/src/charts/treemap.ts b/packages/sanddance-specs/src/charts/treemap.ts
index e57158a25..1a36c6d8f 100644
--- a/packages/sanddance-specs/src/charts/treemap.ts
+++ b/packages/sanddance-specs/src/charts/treemap.ts
@@ -4,9 +4,9 @@ import { AxisScales } from '../interfaces';
 import { SignalNames } from '../constants';
 import { SpecBuilderProps } from '../specBuilder';
 import { SpecContext } from '../types';
-import { Treemap, TreemapProps } from '../layouts/treemap';
+import { TreemapProps } from '../layouts/treemap';
 import { LayoutPair } from '../layouts/layout';
-import { AggregateContainer, AggregateContainerProps } from '../layouts/aggregateContainer';
+import { AggregateContainerProps } from '../layouts/aggregateContainer';
 
 export default function (specContext: SpecContext): SpecBuilderProps {
     const { specColumns, specViewOptions } = specContext;
@@ -37,12 +37,12 @@ export default function (specContext: SpecContext): SpecBuilderProps {
             showAxes: false
         };
         layouts.push({
-            layoutClass: AggregateContainer,
+            layoutType: 'AggregateContainer',
             props
         });
     }
     layouts.push({
-        layoutClass: Treemap,
+        layoutType: 'Treemap',
         props: treemapProps
     });
     return {
@@ -61,6 +61,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
                 },
                 {
                     role: 'z',
+                    axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
                     allowNone: true
                 },
                 {
diff --git a/packages/sanddance-specs/src/facetLayout.ts b/packages/sanddance-specs/src/facetLayout.ts
index 2fd82162a..42f489ee2 100644
--- a/packages/sanddance-specs/src/facetLayout.ts
+++ b/packages/sanddance-specs/src/facetLayout.ts
@@ -1,6 +1,6 @@
 // Copyright (c) Microsoft Corporation. All rights reserved.
 // Licensed under the MIT license.
-import { Cross, CrossProps } from './layouts/cross';
+import { CrossProps } from './layouts/cross';
 import { DiscreteColumn } from './interfaces';
 import {
     facetPaddingBottom,
@@ -10,26 +10,32 @@ import {
 } from './defaults';
 import { FacetStyle } from './types';
 import { LayoutPair } from './layouts/layout';
-import { Scale, Signal } from 'vega-typings';
-import { SignalNames } from './constants';
-import { Wrap, WrapProps } from './layouts/wrap';
+import { WrapProps } from './layouts/wrap';
+
+export interface PlotPadding {
+    x: number;
+    y: number;
+}
+
+export interface FacetPadding {
+    top: number;
+    left: number;
+    bottom: number;
+}
 
 export interface FacetLayout {
-    layoutPair: LayoutPair;
-    plotPadding: { x: number, y: number };
-    scales: Scale[];
-    signals: Signal[];
+    facetPadding: FacetPadding;
+    plotPadding: PlotPadding;
 }
 
-export function getFacetLayout(facetStyle: FacetStyle, facetColumn: DiscreteColumn, facetVColumn: DiscreteColumn, axisTextColor: string): FacetLayout {
+export function getFacetLayout(facetStyle: FacetStyle, facetColumn: DiscreteColumn, facetVColumn: DiscreteColumn, axisTextColor: string) {
     let layoutPair: LayoutPair;
-    const scales: Scale[] = [];
-    let signals: Signal[];
     const groupby = facetColumn;
-    const plotPadding = {
+    const plotPadding: PlotPadding = {
         x: 0,
         y: 0
     };
+    let facetPadding: FacetPadding;
     switch (facetStyle) {
         case 'cross': {
             const props: CrossProps = {
@@ -39,23 +45,14 @@ export function getFacetLayout(facetStyle: FacetStyle, facetColumn: DiscreteColu
                 groupbyY: facetVColumn
             };
             layoutPair = {
-                layoutClass: Cross,
+                layoutType: 'Cross',
                 props
             };
-            signals = [
-                {
-                    name: SignalNames.FacetPaddingBottom,
-                    update: `${facetPaddingBottom}`
-                },
-                {
-                    name: SignalNames.FacetPaddingLeft,
-                    update: `${facetPaddingLeft}`
-                },
-                {
-                    name: SignalNames.FacetPaddingTop,
-                    update: '0'
-                }
-            ];
+            facetPadding = {
+                bottom: facetPaddingBottom,
+                left: facetPaddingLeft,
+                top: 0
+            };
             plotPadding.y = facetPaddingTop;
             plotPadding.x = facetPaddingRight;
             break;
@@ -68,26 +65,25 @@ export function getFacetLayout(facetStyle: FacetStyle, facetColumn: DiscreteColu
                 groupby
             };
             layoutPair = {
-                layoutClass: Wrap,
+                layoutType: 'Wrap',
                 props
             };
-            signals = [
-                {
-                    name: SignalNames.FacetPaddingBottom,
-                    update: `${facetPaddingBottom}`
-                },
-                {
-                    name: SignalNames.FacetPaddingLeft,
-                    update: `${facetPaddingLeft}`
-                },
-                {
-                    name: SignalNames.FacetPaddingTop,
-                    update: `${facetPaddingTop}`
-                }
-            ];
+            facetPadding =
+            {
+                bottom: facetPaddingBottom,
+                left: facetPaddingLeft,
+                top: facetPaddingTop
+            };
             break;
         }
     }
-    return { layoutPair, plotPadding, scales, signals };
+    const facetLayout: FacetLayout = {
+        facetPadding,
+        plotPadding
+    };
+    return {
+        layoutPair,
+        facetLayout
+    };
 }
 
diff --git a/packages/sanddance-specs/src/layouts/aggregateSquare.ts b/packages/sanddance-specs/src/layouts/aggregateSquare.ts
index 682aefa99..9ad27a803 100644
--- a/packages/sanddance-specs/src/layouts/aggregateSquare.ts
+++ b/packages/sanddance-specs/src/layouts/aggregateSquare.ts
@@ -81,10 +81,6 @@ export class AggregateSquare extends Layout {
                 layoutHeight: null,
                 layoutWidth: null
             },
-            globalScales: {
-                showAxes: false,
-                scales: {}
-            },
             encodingRuleMap: {
                 y: [{
                     test: testForCollapseSelection(),
diff --git a/packages/sanddance-specs/src/layouts/band.ts b/packages/sanddance-specs/src/layouts/band.ts
index 83790b3aa..baa4f4fdc 100644
--- a/packages/sanddance-specs/src/layouts/band.ts
+++ b/packages/sanddance-specs/src/layouts/band.ts
@@ -1,7 +1,7 @@
 // Copyright (c) Microsoft Corporation. All rights reserved.
 // Licensed under the MIT license.
 import { Layout, LayoutBuildProps, LayoutProps } from './layout';
-import { AugmentBinnable, binnable, Binnable } from '../bin';
+import { binnable, Binnable } from '../bin';
 import { safeFieldName } from '../expr';
 import {
     DiscreteColumn,
@@ -18,7 +18,7 @@ import {
 } from '../scope';
 import { testForCollapseSelection } from '../selection';
 import { modifySignal } from '../signals';
-import { BandScale, LinearScale, Scale } from 'vega-typings';
+import { BandScale, Scale } from 'vega-typings';
 
 export interface BandProps extends LayoutProps {
     excludeEncodingRuleMap?: boolean;
@@ -178,55 +178,6 @@ export class Band extends Layout {
         const binField = safeFieldName(bin.fields[0]);
 
         const scales: Scale[] = [];
-
-        // function axisScaleName(scaleName: string) {
-        //     return `${scaleName}_axis`;
-        // }
-
-        // if (bin.discreteColumn.column.quantitative) {
-        //     const { binSignal } = <AugmentBinnable>bin;
-
-        //     let linearScale: LinearScale;
-        //     if (horizontal) {
-        //         linearScale = {
-        //             type: 'linear',
-        //             name: axisScaleName(names.yScale),
-        //             range: [
-        //                 0,
-        //                 {
-        //                     signal: parentScope.sizeSignals.layoutHeight
-        //                 }
-        //             ],
-        //             domain: {
-        //                 signal: `[${binSignal}.start, ${binSignal}.stop]`
-        //             },
-        //             bins: {
-        //                 signal: binSignal
-        //             },
-        //             reverse: true,
-        //             zero: false
-        //         };
-        //     } else {
-        //         linearScale = {
-        //             type: 'linear',
-        //             name: axisScaleName(names.xScale),
-        //             range: [
-        //                 0,
-        //                 {
-        //                     signal: parentScope.sizeSignals.layoutWidth
-        //                 }
-        //             ],
-        //             domain: {
-        //                 signal: `[${binSignal}.start, ${binSignal}.stop]`
-        //             },
-        //             bins: {
-        //                 signal: binSignal
-        //             },
-        //             zero: false
-        //         };
-        //     }
-        //     scales.push(linearScale);
-        // }
         let bandScale: BandScale;
         if (horizontal) {
             bandScale = {
diff --git a/packages/sanddance-specs/src/layouts/index.ts b/packages/sanddance-specs/src/layouts/index.ts
new file mode 100644
index 000000000..e38c26c98
--- /dev/null
+++ b/packages/sanddance-specs/src/layouts/index.ts
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+import { AggregateContainer } from './aggregateContainer';
+import { AggregateSquare } from './aggregateSquare';
+import { Band } from './band';
+import { Cross } from './cross';
+import { Layout } from './layout';
+import { Scatter } from './scatter';
+import { Square } from './square';
+import { Stack } from './stack';
+import { Strip } from './strip';
+import { Treemap } from './treemap';
+import { Wrap } from './wrap';
+
+export type LayoutType =
+    'AggregateContainer' |
+    'AggregateSquare' |
+    'Band' |
+    'Cross' |
+    'Scatter' |
+    'Square' |
+    'Stack' |
+    'Strip' |
+    'Treemap' |
+    'Wrap'
+    ;
+
+export const layoutClasses: { [key in LayoutType]: typeof Layout } = {
+    AggregateContainer,
+    AggregateSquare,
+    Band,
+    Cross,
+    Scatter,
+    Square,
+    Stack,
+    Strip,
+    Treemap,
+    Wrap
+};
diff --git a/packages/sanddance-specs/src/layouts/layout.ts b/packages/sanddance-specs/src/layouts/layout.ts
index fe1aae8b0..0928a76ae 100644
--- a/packages/sanddance-specs/src/layouts/layout.ts
+++ b/packages/sanddance-specs/src/layouts/layout.ts
@@ -1,5 +1,6 @@
 // Copyright (c) Microsoft Corporation. All rights reserved.
 // Licensed under the MIT license.
+import { LayoutType } from './index';
 import { GlobalScope } from '../globalScope';
 import {
     AxisScales,
@@ -13,7 +14,7 @@ export interface LayoutProps {
 
 export interface LayoutPair {
     props?: LayoutProps;
-    layoutClass: typeof Layout;
+    layoutType: LayoutType;
 }
 
 export interface LayoutBuildProps {
diff --git a/packages/sanddance-specs/src/layouts/slice.ts b/packages/sanddance-specs/src/layouts/slice.ts
deleted file mode 100644
index d5420c38b..000000000
--- a/packages/sanddance-specs/src/layouts/slice.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-import { Layout, LayoutBuildProps, LayoutProps } from './layout';
-import { binnable, Binnable } from '../bin';
-import { DiscreteColumn, InnerScope, Orientation } from '../interfaces';
-import { addTransforms } from '../scope';
-import { Mark } from 'vega-typings';
-
-export interface SliceProps extends LayoutProps {
-    groupby: DiscreteColumn;
-    orientation: Orientation;
-}
-
-export class Slice extends Layout {
-    private bin: Binnable;
-
-    constructor(public props: SliceProps & LayoutBuildProps) {
-        super(props);
-        this.prefix = `slice_${this.id}`;
-        this.bin = binnable(this.prefix, props.globalScope.data.name, props.groupby);
-    }
-
-    public getGrouping() {
-        return this.bin.fields;
-    }
-
-    public build(): InnerScope {
-        const { bin, prefix, props } = this;
-        const { globalScope, parentScope } = props;
-        const facetDataName = `data_${prefix}_facet`;
-
-        if (bin.native === false) {
-            globalScope.scope.signals.push(...bin.signals);
-            addTransforms(globalScope.data, ...bin.transforms);
-            globalScope.scope.data.push(bin.dataSequence);
-        }
-        // const mark: Mark = {
-        //     style: 'cell',
-        //     name: prefix,
-        //     type: 'group',
-        //     from: {
-        //         facet: {
-        //             name: facetDataName,
-        //             data: parentScope.dataName,
-        //             groupby: bin.fields
-        //         }
-        //     },
-        //     encode: {
-        //     },
-        //     marks: [
-        //         {
-        //             type: 'text',
-        //             encode: {
-        //                 update: {
-        //                     text: {
-        //                         signal: `length(data(${JSON.stringify(facetDataName)}))`
-        //                     },
-        //                     fontSize: {
-        //                         value: 20
-        //                     }
-        //                 }
-        //             }
-        //         }
-        //     ]
-        // };
-        // parentScope.scope.marks.push(mark);
-
-        return {
-            offsets: null,
-            sizeSignals: { layoutHeight: 'TODO', layoutWidth: 'TODO' }
-        };
-
-    }
-}
diff --git a/packages/sanddance-specs/src/layouts/square.ts b/packages/sanddance-specs/src/layouts/square.ts
index fbdbb95c1..075944c4e 100644
--- a/packages/sanddance-specs/src/layouts/square.ts
+++ b/packages/sanddance-specs/src/layouts/square.ts
@@ -50,7 +50,7 @@ export class Square extends Layout {
     public build(): InnerScope {
         const { names, prefix, props } = this;
         const { fillDirection, globalScope, groupings, parentScope, collapseYHeight, sortBy, z } = props;
-        addZScale(z, globalScope.zSize, globalScope, names.zScale);
+        const zScale = addZScale(z, globalScope.zSize, globalScope.data.name, names.zScale);
 
         addTransforms(globalScope.data, {
             type: 'stack',
@@ -112,6 +112,14 @@ export class Square extends Layout {
         const { tx, ty } = this.transformXY(gap, levelSize, squaresPerBand);
 
         return {
+            ...z && {
+                globalScales: {
+                    showAxes: true,
+                    scales: {
+                        z: [zScale]
+                    }
+                }
+            },
             offsets: {
                 x: addOffsets(parentScope.offsets.x, tx.expr),
                 y: addOffsets(parentScope.offsets.y, ty.expr),
diff --git a/packages/sanddance-specs/src/layouts/stack.ts b/packages/sanddance-specs/src/layouts/stack.ts
index fe92f5571..9271db476 100644
--- a/packages/sanddance-specs/src/layouts/stack.ts
+++ b/packages/sanddance-specs/src/layouts/stack.ts
@@ -13,7 +13,7 @@ import {
 } from '../scope';
 import { testForCollapseSelection } from '../selection';
 import { Column } from '@msrvida/chart-types';
-import { RectMark } from 'vega-typings';
+import { LinearScale, RectMark } from 'vega-typings';
 
 export interface StackProps extends LayoutProps {
     sort: Column;
@@ -32,7 +32,9 @@ export class Stack extends Layout {
         sides: string,
         size: string,
         squared: string,
-        squaredExtent: string
+        maxCount: string,
+        maxLevels: string,
+        zScale: string,
     };
 
     constructor(public props: StackProps & LayoutBuildProps) {
@@ -50,7 +52,9 @@ export class Stack extends Layout {
             sides: `${p}_sides`,
             size: `${p}_size`,
             squared: `${p}_squared`,
-            squaredExtent: `${p}_squared_extent`
+            maxCount: `${p}_maxCount`,
+            maxLevels: `${p}_maxLevels`,
+            zScale: `${p}_zScale`,
         };
     }
 
@@ -134,11 +138,6 @@ export class Stack extends Layout {
                     {
                         type: 'filter',
                         expr: 'datum.row_number === 1'
-                    },
-                    {
-                        type: 'extent',
-                        field: 'squared',
-                        signal: names.squaredExtent
                     }
                 ]
             }
@@ -151,7 +150,7 @@ export class Stack extends Layout {
             },
             {
                 name: names.squared,
-                update: `${names.squaredExtent}[0]`
+                update: `data('${names.sequence}')[0].squared`
             },
             {
                 name: names.sides,
@@ -160,6 +159,14 @@ export class Stack extends Layout {
             {
                 name: names.cube,
                 update: `(${names.size} - (${names.sides} - 1)) / ${names.sides}`
+            },
+            {
+                name: names.maxLevels,
+                update: `data('${names.sequence}')[0].maxlevels`
+            },
+            {
+                name: names.maxCount,
+                update: `${names.maxLevels} * ${names.squared}`
             }
         );
 
@@ -199,6 +206,24 @@ export class Stack extends Layout {
         };
         addMarks(globalScope.markGroup, mark);
 
+        const zScale: LinearScale = {
+            type: 'linear',
+            name: names.zScale,
+            domain: [
+                0,
+                {
+                    signal: names.maxCount
+                }
+            ],
+            range: [
+                0,
+                {
+                    signal: `${names.maxLevels} * (${names.cube} + 1) - 1`
+                }
+            ],
+            nice: false
+        };
+
         return {
             offsets,
             mark,
@@ -207,8 +232,10 @@ export class Stack extends Layout {
                 layoutWidth: names.size
             },
             globalScales: {
-                showAxes: false,
-                scales: {}
+                showAxes: true,
+                scales: {
+                    z: [zScale]
+                }
             },
             encodingRuleMap: {
                 y: [{
diff --git a/packages/sanddance-specs/src/layouts/strip.ts b/packages/sanddance-specs/src/layouts/strip.ts
index 03050073b..7179acc0e 100644
--- a/packages/sanddance-specs/src/layouts/strip.ts
+++ b/packages/sanddance-specs/src/layouts/strip.ts
@@ -55,7 +55,7 @@ export class Strip extends Layout {
         const { names, prefix, props } = this;
         const { addPercentageScale, globalScope, groupings, orientation, size, sort, sortOrder, parentScope, z } = props;
 
-        addZScale(z, globalScope.zSize, globalScope, names.zScale);
+        const zScale = addZScale(z, globalScope.zSize, globalScope.data.name, names.zScale);
 
         const horizontal = orientation === 'horizontal';
 
@@ -182,7 +182,8 @@ export class Strip extends Layout {
                 showAxes: true,
                 scales: {
                     x: horizontal ? [percentageScale] : undefined,
-                    y: horizontal ? undefined : [percentageScale]
+                    y: horizontal ? undefined : [percentageScale],
+                    z: zScale && [zScale]
                 }
             },
             offsets,
diff --git a/packages/sanddance-specs/src/layouts/treemap.ts b/packages/sanddance-specs/src/layouts/treemap.ts
index 3b39e080c..d0c7a7971 100644
--- a/packages/sanddance-specs/src/layouts/treemap.ts
+++ b/packages/sanddance-specs/src/layouts/treemap.ts
@@ -77,7 +77,7 @@ export class Treemap extends Layout {
         const { names, props } = this;
         const { globalScope, parentScope, treeMapMethod, z } = props;
 
-        addZScale(z, globalScope.zSize, globalScope, names.zScale);
+        const zScale = addZScale(z, globalScope.zSize, globalScope.data.name, names.zScale);
 
         const offsets: LayoutOffsets = {
             x: addOffsets(parentScope.offsets.x, fn(names.fieldX0)),
@@ -101,6 +101,14 @@ export class Treemap extends Layout {
         });
 
         return {
+            ...z && {
+                globalScales: {
+                    showAxes: true,
+                    scales: {
+                        z: [zScale]
+                    }
+                }
+            },
             mark,
             offsets,
             sizeSignals: {
diff --git a/packages/sanddance-specs/src/scope.ts b/packages/sanddance-specs/src/scope.ts
index cb8626784..b86399df5 100644
--- a/packages/sanddance-specs/src/scope.ts
+++ b/packages/sanddance-specs/src/scope.ts
@@ -36,7 +36,7 @@ export function addScales(scope: Scope, ...scale: Scale[]) {
     if (!scope.scales) {
         scope.scales = [];
     }
-    scope.scales.push(...scale);
+    scope.scales.push(...scale.filter(Boolean));
 }
 
 export function addSignals(scope: Scope, ...signal: Signal[]) {
diff --git a/packages/sanddance-specs/src/specBuilder.ts b/packages/sanddance-specs/src/specBuilder.ts
index 01a7ba246..96e0cf87b 100644
--- a/packages/sanddance-specs/src/specBuilder.ts
+++ b/packages/sanddance-specs/src/specBuilder.ts
@@ -9,29 +9,24 @@ import {
     axesTitlePaddingFacetX,
     axesTitlePaddingFacetY,
     axesTitlePaddingX,
-    axesTitlePaddingY,
-    defaultBins,
-    maxbins
+    axesTitlePaddingY
 } from './defaults';
 import { minFacetHeight, minFacetWidth } from './defaults';
-import { FacetLayout, getFacetLayout } from './facetLayout';
+import { FacetLayout } from './facetLayout';
 import { addFacetAxesGroupMarks } from './facetTitle';
 import { fill, opacity } from './fill';
 import { GlobalScope, GlobalSignals } from './globalScope';
 import {
     AxisScales,
-    DiscreteColumn,
     EncodingRule,
     GlobalScales,
     Grouping,
     InnerScope,
-    LayoutOffsets,
     SpecResult
 } from './interfaces';
 import { LayoutBuildProps, LayoutPair, LayoutProps } from './layouts/layout';
 import {
     addData,
-    addScales,
     addSignals
 } from './scope';
 import { textSignals } from './signals';
@@ -41,6 +36,7 @@ import {
     LinearScale,
     Spec
 } from 'vega-typings';
+import { layoutClasses } from './layouts/index';
 
 export interface SpecBuilderProps {
     axisScales?: AxisScales;
@@ -48,12 +44,13 @@ export interface SpecBuilderProps {
     errors?: string[];
     specCapabilities: SpecCapabilities;
     customZScale?: boolean;
+    facetLayout?: FacetLayout;
 }
 
 export class SpecBuilder {
     private globalSignals: GlobalSignals;
 
-    constructor(public props: SpecBuilderProps & { specContext: SpecContext }) {
+    constructor(public props: SpecBuilderProps, public specContext: SpecContext) {
         this.globalSignals = {
             minCellWidth: {
                 name: SignalNames.MinCellWidth,
@@ -70,7 +67,8 @@ export class SpecBuilder {
     }
 
     public validate() {
-        const { specCapabilities, specContext } = this.props;
+        const { specContext } = this;
+        const { specCapabilities } = this.props;
         const { roles } = specCapabilities;
         const required = roles.filter(r => {
             switch (typeof r.allowNone) {
@@ -109,186 +107,174 @@ export class SpecBuilder {
     }
 
     public build(): SpecResult {
-        const { specCapabilities } = this.props;
-        const errors = this.validate();
-        if (errors.length) {
-            return {
-                errors,
-                specCapabilities,
-                vegaSpec: null
-            };
-        } else {
-            const { specContext } = this.props;
-            const { insight, specColumns, specViewOptions } = specContext;
-            const dataName = 'data_source';
-            const { vegaSpec, groupMark } = this.initSpec(dataName);
-            const { topColorField, colorDataName } = addColor({
-                scope: vegaSpec,
-                dataName,
-                specContext,
-                scaleName: ScaleNames.Color,
-                legendDataName: 'data_legend',
-                topLookupName: 'data_topcolorlookup',
-                colorReverseSignalName: SignalNames.ColorReverse
-            });
-            const globalScope = new GlobalScope({
-                dataName: colorDataName,
-                markGroup: groupMark,
-                scope: vegaSpec,
-                signals: this.globalSignals
-            });
-            let facetLayout: FacetLayout;
-            if (insight.columns.facet) {
-                const discreteFacetColumn: DiscreteColumn = {
-                    column: specColumns.facet,
-                    defaultBins,
-                    maxbins,
-                    maxbinsSignalDisplayName: specViewOptions.language.FacetMaxBins,
-                    maxbinsSignalName: SignalNames.FacetBins
-                };
-                const discreteFacetVColumn: DiscreteColumn = {
-                    column: specColumns.facetV,
-                    defaultBins,
-                    maxbins,
-                    maxbinsSignalDisplayName: specViewOptions.language.FacetVMaxBins,
-                    maxbinsSignalName: SignalNames.FacetVBins
-                };
-                facetLayout = getFacetLayout(insight.facetStyle, discreteFacetColumn, discreteFacetVColumn, specViewOptions.colors.axisText);
-                addSignals(vegaSpec, ...facetLayout.signals);
-                addScales(vegaSpec, ...facetLayout.scales);
-                this.props.layouts = [facetLayout.layoutPair, ...this.props.layouts];
-                this.globalSignals.plotOffsetTop.update = `${facetLayout.plotPadding.y}`;
-                this.globalSignals.plotOffsetRight.update = `${facetLayout.plotPadding.x}`;
-            }
-            const {
-                firstScope,
-                finalScope,
-                specResult,
-                allGlobalScales,
-                allEncodingRules
-            } = this.iterateLayouts(globalScope, (i, innerScope) => {
-                if (facetLayout && i === 0) {
-                    globalScope.zSize = innerScope.offsets.h;
-                }
-            });
+        const { specContext } = this;
 
-            if (specResult) {
-                return specResult;
+        const { facetLayout, specCapabilities  } = this.props;
+        const { insight, specColumns, specViewOptions } = specContext;
+        const dataName = 'data_source';
+        const { vegaSpec, groupMark } = this.initSpec(dataName);
+        const { topColorField, colorDataName } = addColor({
+            scope: vegaSpec,
+            dataName,
+            specContext,
+            scaleName: ScaleNames.Color,
+            legendDataName: 'data_legend',
+            topLookupName: 'data_topcolorlookup',
+            colorReverseSignalName: SignalNames.ColorReverse
+        });
+        const globalScope = new GlobalScope({
+            dataName: colorDataName,
+            markGroup: groupMark,
+            scope: vegaSpec,
+            signals: this.globalSignals
+        });
+        if (facetLayout) {
+            addSignals(vegaSpec,
+                {
+                    name: SignalNames.FacetPaddingBottom,
+                    update: `${facetLayout.facetPadding.bottom}`
+                },
+                {
+                    name: SignalNames.FacetPaddingLeft,
+                    update: `${facetLayout.facetPadding.left}`
+                },
+                {
+                    name: SignalNames.FacetPaddingTop,
+                    update: `${facetLayout.facetPadding.top}`
+                }
+            );
+            this.globalSignals.plotOffsetTop.update = `${facetLayout.plotPadding.y}`;
+            this.globalSignals.plotOffsetRight.update = `${facetLayout.plotPadding.x}`;
+        }
+        const {
+            firstScope,
+            finalScope,
+            specResult,
+            allGlobalScales,
+            allEncodingRules
+        } = this.iterateLayouts(globalScope, (i, innerScope) => {
+            if (facetLayout && i === 0) {
+                globalScope.zSize = innerScope.offsets.h;
             }
-            if (allGlobalScales.length > 0) {
-                const plotHeightOut = this.globalSignals.plotHeightOut.name;
-                const plotWidthOut = this.globalSignals.plotWidthOut.name;
-
-                const colTitleScale: LinearScale = {
-                    type: 'linear',
-                    name: 'scale_facet_col_title',
-                    domain: [0, 1],
-                    range: [0, { signal: plotWidthOut }]
-                };
+        });
 
-                const rowTitleScale: LinearScale = {
-                    type: 'linear',
-                    name: 'scale_facet_row_title',
-                    domain: [0, 1],
-                    range: [{ signal: plotHeightOut }, 0]
-                };
+        if (specResult) {
+            return specResult;
+        }
+        if (allGlobalScales.length > 0) {
+            const plotHeightOut = this.globalSignals.plotHeightOut.name;
+            const plotWidthOut = this.globalSignals.plotWidthOut.name;
 
-                let axesScopes: AxesScopeMap = facetLayout ?
-                    addFacetAxesGroupMarks({
-                        globalScope: globalScope.scope,
-                        plotScope: groupMark,
-                        facetScope: firstScope,
-                        colTitleScale,
-                        rowTitleScale,
-                        colSeqName: 'data_FacetCellColTitles',
-                        rowSeqName: 'data_FacetCellRowTitles'
-                    })
-                    :
-                    {
-                        main: [{
-                            scope: groupMark,
-                            lines: true,
-                            labels: true,
-                            title: true
-                        }]
-                    };
-                addGlobalAxes({
-                    globalScope,
-                    allGlobalScales,
-                    axisScales: this.props.axisScales,
-                    plotOffsetSignals: { x: this.globalSignals.plotOffsetLeft, y: this.globalSignals.plotOffsetBottom },
-                    axesOffsets: { x: axesOffsetX, y: axesOffsetY },
-                    axesTitlePadding: facetLayout ? { x: axesTitlePaddingFacetX, y: axesTitlePaddingFacetY } : { x: axesTitlePaddingX, y: axesTitlePaddingY },
-                    labelBaseline: { x: 'top', y: 'middle' },
-                    specColumns,
-                    specViewOptions,
-                    axesScopes
-                });
-            }
+            const colTitleScale: LinearScale = {
+                type: 'linear',
+                name: 'scale_facet_col_title',
+                domain: [0, 1],
+                range: [0, { signal: plotWidthOut }]
+            };
 
-            //add mark to the final scope
-            if (finalScope.mark) {
-                const { update } = finalScope.mark.encode;
+            const rowTitleScale: LinearScale = {
+                type: 'linear',
+                name: 'scale_facet_row_title',
+                domain: [0, 1],
+                range: [{ signal: plotHeightOut }, 0]
+            };
 
-                const outputDataName = 'output';
-                finalScope.mark.from.data = outputDataName;
-                addData(globalScope.markGroup,
-                    {
-                        name: outputDataName,
-                        source: globalScope.markDataName,
-                        transform: [
-                            {
-                                type: 'formula',
-                                expr: finalScope.offsets.x,
-                                as: FieldNames.OffsetX
-                            },
-                            {
-                                type: 'formula',
-                                expr: finalScope.offsets.y,
-                                as: FieldNames.OffsetY
-                            }
-                        ]
-                    }
-                );
-                update.x = {
-                    field: FieldNames.OffsetX
-                };
-                update.y = {
-                    field: FieldNames.OffsetY
+            let axesScopes: AxesScopeMap = facetLayout ?
+                addFacetAxesGroupMarks({
+                    globalScope: globalScope.scope,
+                    plotScope: groupMark,
+                    facetScope: firstScope,
+                    colTitleScale,
+                    rowTitleScale,
+                    colSeqName: 'data_FacetCellColTitles',
+                    rowSeqName: 'data_FacetCellRowTitles'
+                })
+                :
+                {
+                    main: [{
+                        scope: groupMark,
+                        lines: true,
+                        labels: true,
+                        title: true
+                    }]
                 };
+            addGlobalAxes({
+                globalScope,
+                allGlobalScales,
+                axisScales: this.props.axisScales,
+                plotOffsetSignals: { x: this.globalSignals.plotOffsetLeft, y: this.globalSignals.plotOffsetBottom },
+                axesOffsets: { x: axesOffsetX, y: axesOffsetY },
+                axesTitlePadding: facetLayout ? { x: axesTitlePaddingFacetX, y: axesTitlePaddingFacetY } : { x: axesTitlePaddingX, y: axesTitlePaddingY },
+                labelBaseline: { x: 'top', y: 'middle' },
+                specColumns,
+                specViewOptions,
+                axesScopes,
+                view: insight.view
+            });
+        }
+
+        //add mark to the final scope
+        if (finalScope.mark) {
+            const { update } = finalScope.mark.encode;
 
-                allEncodingRules.forEach(map => {
-                    for (let key in map) {
-                        if (update[key]) {
-                            let arrIn = map[key];
-                            if (!Array.isArray(update[key])) {
-                                let value = update[key];
-                                let arrOut = [];
-                                update[key] = arrOut;
-                                arrIn.forEach(rule => arrOut.push(rule));
-                                arrOut.push(value);
-                            } else {
-                                let arrOut = update[key] as {}[];
-                                arrIn.forEach(rule => arrOut.unshift(rule));
-                            }
+            const outputDataName = 'output';
+            finalScope.mark.from.data = outputDataName;
+            addData(globalScope.markGroup,
+                {
+                    name: outputDataName,
+                    source: globalScope.markDataName,
+                    transform: [
+                        {
+                            type: 'formula',
+                            expr: finalScope.offsets.x,
+                            as: FieldNames.OffsetX
+                        },
+                        {
+                            type: 'formula',
+                            expr: finalScope.offsets.y,
+                            as: FieldNames.OffsetY
+                        }
+                    ]
+                }
+            );
+            update.x = {
+                field: FieldNames.OffsetX
+            };
+            update.y = {
+                field: FieldNames.OffsetY
+            };
+
+            allEncodingRules.forEach(map => {
+                for (let key in map) {
+                    if (update[key]) {
+                        let arrIn = map[key];
+                        if (!Array.isArray(update[key])) {
+                            let value = update[key];
+                            let arrOut = [];
+                            update[key] = arrOut;
+                            arrIn.forEach(rule => arrOut.push(rule));
+                            arrOut.push(value);
+                        } else {
+                            let arrOut = update[key] as {}[];
+                            arrIn.forEach(rule => arrOut.unshift(rule));
                         }
                     }
-                });
+                }
+            });
 
-                update.fill = fill(specContext, topColorField, ScaleNames.Color);
-                update.opacity = opacity(specContext);
-            }
-            return {
-                specCapabilities,
-                vegaSpec
-            };
+            update.fill = fill(specContext, topColorField, ScaleNames.Color);
+            update.opacity = opacity(specContext);
         }
+        return {
+            specCapabilities,
+            vegaSpec
+        };
     }
 
     private initSpec(dataName: string) {
         const { globalSignals } = this;
         const { minCellWidth, minCellHeight, plotOffsetLeft, plotOffsetBottom, plotOffsetTop, plotOffsetRight, plotHeightOut, plotWidthOut } = globalSignals;
-        const { specContext } = this.props;
+        const { specContext } = this;
         const { insight } = specContext;
         const groupMark: GroupMark = {
             type: 'group',
@@ -410,11 +396,12 @@ export class SpecBuilder {
     }
 
     private createLayout(layoutPair: LayoutPair, buildProps: LayoutBuildProps) {
-        const { layoutClass, props } = layoutPair;
+        const { layoutType, props } = layoutPair;
         const layoutBuildProps: LayoutProps & LayoutBuildProps = {
             ...props,
             ...buildProps
         };
+        const layoutClass = layoutClasses[layoutType];
         const layout = new layoutClass(layoutBuildProps);
         layout.id = buildProps.id;
         return layout;
diff --git a/packages/sanddance-specs/src/types.ts b/packages/sanddance-specs/src/types.ts
index 6d530f482..008e954a0 100644
--- a/packages/sanddance-specs/src/types.ts
+++ b/packages/sanddance-specs/src/types.ts
@@ -353,6 +353,11 @@ export interface SpecColumns {
     facetV?: Column;
 }
 
+export interface ZAxisOptions {
+    showZAxis?: boolean;
+    zIndex?: number;
+}
+
 export interface SpecViewOptions {
 
     /**
@@ -370,7 +375,9 @@ export interface SpecViewOptions {
      */
     maxLegends: number;
 
-    tickSize: number
+    tickSize: number;
+
+    zAxisOptions?: ZAxisOptions;
 }
 
 export interface SpecContext {
diff --git a/packages/sanddance-specs/src/zBase.ts b/packages/sanddance-specs/src/zBase.ts
index b9c5ce1cf..6dfde5d99 100644
--- a/packages/sanddance-specs/src/zBase.ts
+++ b/packages/sanddance-specs/src/zBase.ts
@@ -1,19 +1,19 @@
 // Copyright (c) Microsoft Corporation. All rights reserved.
 // Licensed under the MIT license.
 import { SignalNames } from './constants';
-import { GlobalScope } from './globalScope';
 import { linearScale, pointScale } from './scales';
-import { addScales } from './scope';
 import { Column } from '@msrvida/chart-types';
 import { RangeScheme } from 'vega-typings';
 
-export function addZScale(z: Column, zSize: string, globalScope: GlobalScope, zScaleName: string) {
+export function addZScale(z: Column, zSize: string, dataName: string, zScaleName: string) {
     if (z) {
         const zRange: RangeScheme = [0, { signal: `(${zSize}) * ${SignalNames.ZProportion}` }];
-        addScales(globalScope.scope, z.quantitative
+        const scale = z.quantitative
             ?
-            linearScale(zScaleName, globalScope.data.name, z.name, zRange, false, true)
+            linearScale(zScaleName, dataName, z.name, zRange, false, true)
             :
-            pointScale(zScaleName, globalScope.data.name, zRange, z.name, false));
+            pointScale(zScaleName, dataName, zRange, z.name, false)
+            ;
+        return scale;
     }
 }
diff --git a/packages/sanddance/package.json b/packages/sanddance/package.json
index 7ea22d0b7..4e3058508 100644
--- a/packages/sanddance/package.json
+++ b/packages/sanddance/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@msrvida/sanddance",
-  "version": "3.1.1",
+  "version": "3.2.0",
   "description": "SandDance visualization canvas component.",
   "main": "dist/es6/index.js",
   "types": "dist/es6/index.d.ts",
diff --git a/packages/sanddance/src/axes.ts b/packages/sanddance/src/axes.ts
index 67d09a448..a7e4eb372 100644
--- a/packages/sanddance/src/axes.ts
+++ b/packages/sanddance/src/axes.ts
@@ -30,6 +30,7 @@ export function recolorAxes(stage: VegaDeckGl.types.Stage, oldColors: SpecColorS
     let axes: {
         x: VegaDeckGl.types.Axis[];
         y: VegaDeckGl.types.Axis[];
+        z: VegaDeckGl.types.Axis[];
     };
     let textData: VegaDeckGl.types.VegaTextLayerDatum[];
 
@@ -38,7 +39,8 @@ export function recolorAxes(stage: VegaDeckGl.types.Stage, oldColors: SpecColorS
         const textColor = VegaDeckGl.util.colorFromString(newColors.axisText || oldColors.axisText);
         axes = {
             x: cloneAxis(stage.axes.x, lineColor, textColor),
-            y: cloneAxis(stage.axes.y, lineColor, textColor)
+            y: cloneAxis(stage.axes.y, lineColor, textColor),
+            z: cloneAxis(stage.axes.z, lineColor, textColor)
         };
     }
 
diff --git a/packages/sanddance/src/characterSet.ts b/packages/sanddance/src/characterSet.ts
index ad1304bfc..09eaa685a 100644
--- a/packages/sanddance/src/characterSet.ts
+++ b/packages/sanddance/src/characterSet.ts
@@ -16,11 +16,11 @@ export class CharacterSet {
         if (!this.chars) {
             const map: { [char: string]: true } = {};
             const addText = (text: string) => {
-                Array.from(text).forEach(char => { map[char] = true });
+                Array.from(text).forEach(char => { map[char] = true; });
             };
             stage.textData.forEach(t => addText(t.text));
-            const { x, y } = stage.axes;
-            [x, y].forEach(axes => {
+            const { x, y, z } = stage.axes;
+            [x, y, z].forEach(axes => {
                 axes.forEach(axis => {
                     if (axis.tickText) axis.tickText.forEach(t => addText(t.text));
                     if (axis.title) addText(axis.title.text);
diff --git a/packages/sanddance/src/viewer.ts b/packages/sanddance/src/viewer.ts
index 84a83d870..469477bad 100644
--- a/packages/sanddance/src/viewer.ts
+++ b/packages/sanddance/src/viewer.ts
@@ -55,6 +55,8 @@ import { CharacterSet } from './characterSet';
 
 const { defaultView } = VegaDeckGl.defaults;
 
+const zAxisZindex = 1010;
+
 let didRegisterColorSchemes = false;
 
 /**
@@ -280,7 +282,17 @@ export class Viewer {
 
     private async renderNewLayout(signalValues: SignalValues, presenterConfig?: VegaDeckGl.types.PresenterConfig, view?: View) {
         const currData = this._dataScope.currentData();
-        const context: SpecContext = { specColumns: this.getSpecColumnsWithFilteredStats(), insight: this.insight, specViewOptions: this.options };
+        const context: SpecContext = {
+            specColumns: this.getSpecColumnsWithFilteredStats(),
+            insight: this.insight,
+            specViewOptions: {
+                ...this.options,
+                zAxisOptions: {
+                    showZAxis: true,
+                    zIndex: zAxisZindex
+                }
+            }
+        };
         const specResult = build(context, currData);
         if (!specResult.errors) {
             const uiValues = extractSignalValuesFromView(this.vegaViewGl, this.vegaSpec);
@@ -312,7 +324,7 @@ export class Viewer {
 
                 this.vegaSpec.signals.forEach(s => {
                     this.vegaViewGl.addSignalListener(s.name, handler);
-                })
+                });
 
                 //capture new color color contexts via signals
                 this.configForSignalCapture(config.presenterConfig);
@@ -622,6 +634,7 @@ export class Viewer {
     private createConfig(c?: VegaDeckGl.types.PresenterConfig): VegaDeckGl.types.ViewGlConfig {
         const { getTextColor, getTextHighlightColor, getTextHighlightAlphaCutoff, onTextClick } = this.options;
         const defaultPresenterConfig: VegaDeckGl.types.PresenterConfig = {
+            zAxisZindex,
             getCharacterSet: stage => this._characterSet.getCharacterSet(stage),
             getTextColor,
             getTextHighlightColor,
@@ -679,7 +692,7 @@ export class Viewer {
         if (this.options.onBeforeCreateLayers) {
             defaultPresenterConfig.preLayer = stage => {
                 this.options.onBeforeCreateLayers(stage, this.specCapabilities);
-            }
+            };
         }
         const config: VegaDeckGl.types.ViewGlConfig = {
             presenter: this.presenter,
diff --git a/packages/vega-deck.gl/package.json b/packages/vega-deck.gl/package.json
index a0d089405..fdbe3f23f 100644
--- a/packages/vega-deck.gl/package.json
+++ b/packages/vega-deck.gl/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@msrvida/vega-deck.gl",
-  "version": "3.2.1",
+  "version": "3.3.0",
   "description": "Deck.gl renderer for Vega",
   "main": "dist/es6/index.js",
   "files": [
diff --git a/packages/vega-deck.gl/src/defaults.ts b/packages/vega-deck.gl/src/defaults.ts
index c62a95754..d53986e14 100644
--- a/packages/vega-deck.gl/src/defaults.ts
+++ b/packages/vega-deck.gl/src/defaults.ts
@@ -50,7 +50,8 @@ export function createStage(view: View) {
         polygonData: [],
         axes: {
             x: [],
-            y: []
+            y: [],
+            z: [],
         },
         gridLines: [],
         textData: [],
diff --git a/packages/vega-deck.gl/src/interfaces.ts b/packages/vega-deck.gl/src/interfaces.ts
index 2dfcfa095..fb69fc13f 100644
--- a/packages/vega-deck.gl/src/interfaces.ts
+++ b/packages/vega-deck.gl/src/interfaces.ts
@@ -30,11 +30,14 @@ export interface TickText extends VegaTextLayerDatum {
     value: number | string;
 }
 
+export type AxisRole = 'x' | 'y' | 'z';
+
 export interface Axis {
     domain: StyledLine;
     ticks: StyledLine[];
     tickText: TickText[];
     title?: VegaTextLayerDatum;
+    role?: AxisRole;
 }
 
 /**
@@ -69,7 +72,7 @@ export interface Path {
 }
 
 export interface Polygon {
-    positions: Position[];    
+    positions: Position[];
     strokeColor: RGBAColor;
     fillColor: RGBAColor;
     strokeWidth: number;
@@ -102,6 +105,7 @@ export interface Stage {
     axes?: {
         x?: Axis[];
         y?: Axis[];
+        z?: Axis[];
     };
     textData?: VegaTextLayerDatum[];
     view?: View;
@@ -171,6 +175,7 @@ export interface PresenterConfig {
     onSceneRectAssignCubeOrdinal?: (d: object) => number | undefined;
     onTargetViewState?: (height: number, width: number) => { height: number, width: number, newViewStateTarget?: boolean };
     preserveDrawingBuffer?: boolean;
+    zAxisZindex?: number;
 }
 
 export interface PresenterStyle {
diff --git a/packages/vega-deck.gl/src/layers.ts b/packages/vega-deck.gl/src/layers.ts
index 69eb4acfb..ba5f6cd1d 100644
--- a/packages/vega-deck.gl/src/layers.ts
+++ b/packages/vega-deck.gl/src/layers.ts
@@ -32,10 +32,10 @@ export function getLayers(
     guideLines: StyledLine[]
 ): Layer<any>[] {
     const cubeLayer = newCubeLayer(presenter, config, stage.cubeData, presenter.style.highlightColor, lightSettings, lightingMix, interpolator);
-    const { x, y } = stage.axes;
+    const { x, y, z } = stage.axes;
     const lines = concat(stage.gridLines, guideLines);
     const texts = [...stage.textData];
-    [x, y].forEach(axes => {
+    [x, y, z].forEach(axes => {
         axes.forEach(axis => {
             if (axis.domain) lines.push(axis.domain);
             if (axis.ticks) lines.push.apply(lines, axis.ticks);
diff --git a/packages/vega-deck.gl/src/marks/interfaces.ts b/packages/vega-deck.gl/src/marks/interfaces.ts
index d9ab7c203..d99524d70 100644
--- a/packages/vega-deck.gl/src/marks/interfaces.ts
+++ b/packages/vega-deck.gl/src/marks/interfaces.ts
@@ -2,13 +2,14 @@
 // Licensed under the MIT license.
 import { Axis, Stage } from '../interfaces';
 import { RGBAColor } from '@deck.gl/core/utils/color';
-import { Scene, SceneGroup } from 'vega-typings';
+import { Mark, Orient, Scene, SceneGroup } from 'vega-typings';
 
 export enum GroupType {
     none = 0,
     legend = 1,
     xAxis = 2,
-    yAxis = 3
+    yAxis = 3,
+    zAxis = 4,
 }
 
 export interface MarkStagerOptions {
@@ -16,12 +17,14 @@ export interface MarkStagerOptions {
     currAxis: Axis;
     defaultCubeColor: RGBAColor;
     assignCubeOrdinal: (d: object) => number | undefined;
+    zAxisZindex: number;
 }
 
 //TODO - use vega-typings below
-export type SceneGroup2 = SceneGroup & {
+export type AxisSceneGroup = SceneGroup & {
     datum?: any;
-    orient?: 'bottom' | 'left' | 'right' | 'top';
+    orient?: Orient;
+    mark: Mark
 };
 export interface LabelDatum {
     value: any;
diff --git a/packages/vega-deck.gl/src/marks/rule.ts b/packages/vega-deck.gl/src/marks/rule.ts
index b7dbc3423..99d15fee7 100644
--- a/packages/vega-deck.gl/src/marks/rule.ts
+++ b/packages/vega-deck.gl/src/marks/rule.ts
@@ -6,6 +6,7 @@ import { GroupType, MarkStager, MarkStagerOptions } from './interfaces';
 import { lineZ } from '../defaults';
 import { Scene, SceneLine } from 'vega-typings';
 import { Stage, StyledLine } from '../interfaces';
+import { zSwap } from '../zaxis';
 
 const markStager: MarkStager = (options: MarkStagerOptions, stage: Stage, scene: Scene, x: number, y: number, groupType: GroupType) => {
 
@@ -19,8 +20,16 @@ const markStager: MarkStager = (options: MarkStagerOptions, stage: Stage, scene:
         const lineItem = styledLine(x1 + x, y1 + y, x2 + x, y2 + y, item.stroke, item.strokeWidth);
 
         if (item.mark.role === 'axis-tick') {
+            if (options.currAxis.role === 'z') {
+                zSwap(lineItem.sourcePosition);
+                zSwap(lineItem.targetPosition);
+            }
             options.currAxis.ticks.push(lineItem);
         } else if (item.mark.role === 'axis-domain') {
+            if (options.currAxis.role === 'z') {
+                zSwap(lineItem.sourcePosition);
+                zSwap(lineItem.targetPosition);
+            }
             options.currAxis.domain = lineItem;
         } else {
             stage.gridLines.push(lineItem);
diff --git a/packages/vega-deck.gl/src/marks/text.ts b/packages/vega-deck.gl/src/marks/text.ts
index babfe910b..9d82d6a83 100644
--- a/packages/vega-deck.gl/src/marks/text.ts
+++ b/packages/vega-deck.gl/src/marks/text.ts
@@ -16,6 +16,7 @@ import {
     SceneTextBaseline
 } from 'vega-typings';
 import { Stage, TickText, VegaTextLayerDatum } from '../interfaces';
+import { zSwap } from '../zaxis';
 
 interface SceneText2 extends SceneText {
     metaData?: any;
@@ -49,8 +50,14 @@ const markStager: MarkStager = (options: MarkStagerOptions, stage: Stage, scene:
         if (item.mark.role === 'axis-label') {
             const tickText = textItem as TickText;
             tickText.value = (item.datum as LabelDatum).value;
+            if (options.currAxis.role === 'z') {
+                zSwap(tickText.position);
+            }
             options.currAxis.tickText.push(tickText);
         } else if (item.mark.role === 'axis-title') {
+            if (options.currAxis.role === 'z') {
+                zSwap(textItem.position);
+            }
             options.currAxis.title = textItem;
         } else {
             stage.textData.push(textItem);
diff --git a/packages/vega-deck.gl/src/presenter.ts b/packages/vega-deck.gl/src/presenter.ts
index 94f763513..44cf860b3 100644
--- a/packages/vega-deck.gl/src/presenter.ts
+++ b/packages/vega-deck.gl/src/presenter.ts
@@ -160,7 +160,8 @@ export class Presenter {
             maxOrdinal: 0,
             currAxis: null,
             defaultCubeColor: this.style.defaultCubeColor,
-            assignCubeOrdinal: (config && config.onSceneRectAssignCubeOrdinal) || (() => options.maxOrdinal++)
+            assignCubeOrdinal: config?.onSceneRectAssignCubeOrdinal || (() => options.maxOrdinal++),
+            zAxisZindex: config?.zAxisZindex
         };
         //determine if this is a vega scene
         if (scene.marktype) {
@@ -181,7 +182,7 @@ export class Presenter {
 
             let glOptions: WebGLContextAttributes;
             if (config && config.preserveDrawingBuffer) {
-                glOptions = { preserveDrawingBuffer: true }
+                glOptions = { preserveDrawingBuffer: true };
             }
 
             const deckProps: DeckGLInternalProps = {
diff --git a/packages/vega-deck.gl/src/stagers.ts b/packages/vega-deck.gl/src/stagers.ts
index 4978fdc09..2ad10e8c6 100644
--- a/packages/vega-deck.gl/src/stagers.ts
+++ b/packages/vega-deck.gl/src/stagers.ts
@@ -9,6 +9,7 @@ import area from './marks/area';
 
 import {
     Axis,
+    AxisRole,
     FacetRect,
     Stage,
     StyledLine
@@ -20,30 +21,33 @@ import {
     GroupType,
     MarkStager,
     MarkStagerOptions,
-    SceneGroup2
+    AxisSceneGroup
 } from './marks/interfaces';
-import { Scene } from 'vega-typings';
+import { Orient, Scene, SceneGroup } from 'vega-typings';
 
 interface VegaAxisDatum {
     domain: boolean;
     grid: boolean;
     labels: boolean;
-    orient: 'bottom' | 'left' | 'right' | 'top';
+    orient: Orient;
     ticks: boolean;
     title: boolean;
 }
 
-function getOrientItem(group: SceneGroup2): { orient?: 'bottom' | 'left' | 'right' | 'top'; } {
+function getOrientItem(group: AxisSceneGroup): { orient?: Orient; } {
     if (group.orient) {
         return group;
     }
     return group.datum as VegaAxisDatum;
 }
 
-function convertGroupRole(group: SceneGroup2): GroupType {
+function convertGroupRole(group: SceneGroup, options: MarkStagerOptions): GroupType {
     if (group.mark.role === 'legend') return GroupType.legend;
     if (group.mark.role === 'axis') {
-        const orientItem = getOrientItem(group);
+        if (((group as AxisSceneGroup).mark).zindex === options.zAxisZindex && options.zAxisZindex !== undefined) {
+            return GroupType.zAxis;
+        }
+        const orientItem = getOrientItem(group as AxisSceneGroup);
         if (orientItem) {
             switch (orientItem.orient) {
                 case 'bottom':
@@ -59,7 +63,7 @@ function convertGroupRole(group: SceneGroup2): GroupType {
 
 const group: MarkStager = (options: MarkStagerOptions, stage: Stage, scene: Scene, x: number, y: number, groupType: GroupType) => {
 
-    base.vega.sceneVisit(scene, function (g: SceneGroup2) {
+    base.vega.sceneVisit(scene, function (g: SceneGroup) {
 
         const gx = g.x || 0, gy = g.y || 0;
         if (g.context && g.context.background && !stage.backgroundColor) {
@@ -73,7 +77,7 @@ const group: MarkStager = (options: MarkStagerOptions, stage: Stage, scene: Scen
             stage.facets.push(facetRect);
         }
 
-        groupType = convertGroupRole(g) || groupType;
+        groupType = convertGroupRole(g, options) || groupType;
         setCurrentAxis(options, stage, groupType);
 
         // draw group contents
@@ -86,12 +90,19 @@ const group: MarkStager = (options: MarkStagerOptions, stage: Stage, scene: Scen
 
 function setCurrentAxis(options: MarkStagerOptions, stage: Stage, groupType: GroupType) {
     let axes: Axis[];
+    let role: AxisRole;
     switch (groupType) {
         case GroupType.xAxis:
             axes = stage.axes.x;
+            role = 'x';
             break;
         case GroupType.yAxis:
             axes = stage.axes.y;
+            role = 'y';
+            break;
+        case GroupType.zAxis:
+            axes = stage.axes.z;
+            role = 'z';
             break;
         default:
             return;
@@ -99,7 +110,8 @@ function setCurrentAxis(options: MarkStagerOptions, stage: Stage, groupType: Gro
     options.currAxis = {
         domain: null,
         tickText: [],
-        ticks: []
+        ticks: [],
+        role
     };
     axes.push(options.currAxis);
 }
diff --git a/packages/vega-deck.gl/src/zaxis.ts b/packages/vega-deck.gl/src/zaxis.ts
new file mode 100644
index 000000000..b6aabd68e
--- /dev/null
+++ b/packages/vega-deck.gl/src/zaxis.ts
@@ -0,0 +1,14 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+import { Position } from '@deck.gl/core';
+import { lineZ } from './defaults';
+import { Vec3 } from './interfaces';
+
+export function zSwap(v3: Vec3 | Position) {
+    let temp = -v3[1]; //negeative y to positive z
+    if(v3[0]===lineZ) {
+        v3[0] = 0;
+    }
+    v3[1] = v3[2];
+    v3[2] = temp;
+}
diff --git a/packages/vega-lite-unit/src/bar.ts b/packages/vega-lite-unit/src/bar.ts
index d12c2e37c..9fdd97e0f 100644
--- a/packages/vega-lite-unit/src/bar.ts
+++ b/packages/vega-lite-unit/src/bar.ts
@@ -70,7 +70,7 @@ export function unitizeBar(inputSpec: TopLevelUnitSpec, outputSpec: Vega.Spec, u
 
     data0.source = 'source_00';
     delete data0Url.url;
-    delete data0.format;
+    delete data0Url.format;
 
     if (info.quantitativeBand) {
         bandBinTransform = findBinTransform(data0, info.bandEncoding.field).transform;


Test patch:
diff --git a/docs/tests/sanddance-specs/v1/index.html b/docs/tests/sanddance-specs/v1/index.html
index 176a4a85c..59c7ee3a3 100644
--- a/docs/tests/sanddance-specs/v1/index.html
+++ b/docs/tests/sanddance-specs/v1/index.html
@@ -56,6 +56,7 @@ <h1>SandDance-Specs test</h1>
                 <option>column.json</option>
                 <option>density-facet-cross.json</option>
                 <option>density-facet-wrap.json</option>
+                <option>density-treemap.json</option>
                 <option>density.json</option>
                 <option>scatter-facet-cross.json</option>
                 <option>scatter-facet-wrap.json</option>
diff --git a/docs/tests/sanddance-specs/v1/specs/density-treemap.json b/docs/tests/sanddance-specs/v1/specs/density-treemap.json
new file mode 100644
index 000000000..92567e2a3
--- /dev/null
+++ b/docs/tests/sanddance-specs/v1/specs/density-treemap.json
@@ -0,0 +1,19 @@
+{
+    "columns": {
+        "x": "Longitude",
+        "y": "Latitude",
+        "color": "Obama",
+        "z": "Education",
+        "sort": "State",
+        "size": "TotalPop"
+    },
+    "facetStyle": "wrap",
+    "scheme": "redblue",
+    "totalStyle": "sum-treemap",
+    "size": {
+        "height": 600,
+        "width": 800
+    },
+    "chart": "density",
+    "view": "2d"
+}
\ No newline at end of file
diff --git a/packages/sanddance-specs/uitest/sanddance-specs.ts b/packages/sanddance-specs/uitest/sanddance-specs.ts
index 6ec9466e6..9ff2fc1e5 100644
--- a/packages/sanddance-specs/uitest/sanddance-specs.ts
+++ b/packages/sanddance-specs/uitest/sanddance-specs.ts
@@ -1,6 +1,6 @@
 // Copyright (c) Microsoft Corporation. All rights reserved.
 // Licensed under the MIT license.
-import { build, getColumnsFromData, getSpecColumns, Insight, SpecContext, SpecResult, SpecViewOptions } from '../dist/es6';
+import { build, getColumnsFromData, getSpecColumns, Insight, SpecContext, SpecViewOptions } from '../src/index';
 import { Column } from '@msrvida/chart-types';
 import * as Vega from 'vega-typings';