ggaction 0.0.11 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +80 -0
- package/README.md +3 -2
- package/knowledge/action-card.schema.json +1 -1
- package/knowledge/action-cards.json +1 -1
- package/knowledge/action-cards.schema.json +1 -1
- package/knowledge/extension-authoring.md +2 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/knowledge/task-resolver.js +1 -1
- package/package.json +4 -2
- package/src/actions/basic.js +2 -0
- package/src/actions/boxPlots/materialize.js +2 -1
- package/src/actions/canvas/actions.js +3 -1
- package/src/actions/canvas/fitting.js +211 -0
- package/src/actions/canvas/index.js +2 -0
- package/src/actions/categoryOrder/index.js +3 -2
- package/src/actions/charts/area.js +47 -0
- package/src/actions/charts/bar.js +11 -5
- package/src/actions/charts/beeswarm.js +70 -0
- package/src/actions/charts/density.js +44 -0
- package/src/actions/charts/ecdf.js +183 -0
- package/src/actions/charts/endpoints.js +451 -0
- package/src/actions/charts/heatmap.js +2 -2
- package/src/actions/charts/histogram.js +1 -1
- package/src/actions/charts/horizon.js +71 -0
- package/src/actions/charts/index.js +37 -0
- package/src/actions/charts/interval-regression.js +169 -0
- package/src/actions/charts/line.js +5 -5
- package/src/actions/charts/parallel.js +1 -1
- package/src/actions/charts/pie.js +44 -0
- package/src/actions/charts/polar.js +123 -0
- package/src/actions/charts/radar.js +299 -0
- package/src/actions/charts/radial.js +42 -0
- package/src/actions/charts/raincloud.js +422 -0
- package/src/actions/charts/rug-strip.js +279 -0
- package/src/actions/charts/scatter.js +9 -3
- package/src/actions/charts/shared.js +69 -10
- package/src/actions/composition/actions.js +118 -1
- package/src/actions/data/bin.js +29 -0
- package/src/actions/data/computed.js +24 -0
- package/src/actions/data/derived.js +66 -0
- package/src/actions/data/ecdf.js +38 -0
- package/src/actions/data/filter.js +212 -28
- package/src/actions/data/fold.js +24 -0
- package/src/actions/data/index.js +24 -0
- package/src/actions/data/interval.js +2 -0
- package/src/actions/data/intervalEdit.js +86 -4
- package/src/actions/data/regression.js +5 -2
- package/src/actions/data/stack.js +26 -0
- package/src/actions/data/summary.js +26 -0
- package/src/actions/data/timeUnit.js +1 -1
- package/src/actions/encodings/appearance.js +32 -13
- package/src/actions/encodings/barWidth.js +8 -0
- package/src/actions/encodings/basic.js +2 -0
- package/src/actions/encodings/color/continuous.js +19 -59
- package/src/actions/encodings/color/index.js +28 -22
- package/src/actions/encodings/color/layout.js +3 -97
- package/src/actions/encodings/color/policy.js +5 -8
- package/src/actions/encodings/density/resolve.js +37 -5
- package/src/actions/encodings/density.js +120 -33
- package/src/actions/encodings/horizon/resolve.js +8 -4
- package/src/actions/encodings/horizon.js +18 -8
- package/src/actions/encodings/index.js +2 -0
- package/src/actions/encodings/offset.js +33 -27
- package/src/actions/encodings/parallel.js +2 -8
- package/src/actions/encodings/position/apply.js +40 -4
- package/src/actions/encodings/position/index.js +43 -5
- package/src/actions/encodings/position/policies/arc.js +37 -2
- package/src/actions/encodings/position/policies/area.js +4 -42
- package/src/actions/encodings/position/policies/bar.js +48 -13
- package/src/actions/encodings/position/resolve.js +79 -24
- package/src/actions/encodings/ranged.js +123 -97
- package/src/actions/encodings/remove.js +37 -32
- package/src/actions/encodings/ruleAppearance.js +23 -9
- package/src/actions/encodings/seriesLayout.js +104 -0
- package/src/actions/encodings/shared.js +25 -23
- package/src/actions/encodings/strokeDash.js +9 -6
- package/src/actions/encodings/temporal.js +7 -0
- package/src/actions/encodings/text.js +30 -15
- package/src/actions/errorBands/create.js +18 -5
- package/src/actions/errorBands/edit.js +341 -21
- package/src/actions/errorBars/create.js +153 -11
- package/src/actions/errorBars/edit.js +365 -19
- package/src/actions/facets/actions.js +410 -12
- package/src/actions/facets/derive.js +57 -8
- package/src/actions/gradientPlots/components.js +33 -11
- package/src/actions/gradientPlots/materialize.js +5 -7
- package/src/actions/guides/applicability.js +20 -17
- package/src/actions/guides/axes/axes.js +4 -3
- package/src/actions/guides/axes/axis.js +18 -18
- package/src/actions/guides/axes/components.js +43 -0
- package/src/actions/guides/axes/edit.js +16 -51
- package/src/actions/guides/axes/labels.js +129 -19
- package/src/actions/guides/axes/lines.js +7 -5
- package/src/actions/guides/axes/parallel/lifecycle.js +117 -0
- package/src/actions/guides/axes/parallel/policy.js +137 -0
- package/src/actions/guides/axes/parallel/resolve.js +44 -23
- package/src/actions/guides/axes/parallel.js +93 -217
- package/src/actions/guides/axes/policy.js +21 -67
- package/src/actions/guides/axes/tickGroups.js +14 -8
- package/src/actions/guides/axes/ticks.js +5 -4
- package/src/actions/guides/axes/titles.js +21 -8
- package/src/actions/guides/basic.js +2 -0
- package/src/actions/guides/facade.js +106 -0
- package/src/actions/guides/facadeAxes.js +200 -0
- package/src/actions/guides/facadeLegend.js +63 -0
- package/src/actions/guides/grids/grid.js +37 -33
- package/src/actions/guides/grids/resolve.js +10 -2
- package/src/actions/guides/guides.js +35 -28
- package/src/actions/guides/legends/categorical/actions.js +193 -249
- package/src/actions/guides/legends/categorical/components.js +4 -5
- package/src/actions/guides/legends/categorical/layout.js +36 -349
- package/src/actions/guides/legends/categorical/options.js +30 -14
- package/src/actions/guides/legends/categorical/recipes.js +14 -0
- package/src/actions/guides/legends/categorical/resolve.js +21 -10
- package/src/actions/guides/legends/categorical/symbols.js +2 -3
- package/src/actions/guides/legends/continuous/common.js +69 -14
- package/src/actions/guides/legends/continuous/gradient.js +111 -83
- package/src/actions/guides/legends/continuous/index.js +6 -2
- package/src/actions/guides/legends/continuous/interval.js +71 -92
- package/src/actions/guides/legends/continuous/opacity.js +105 -84
- package/src/actions/guides/legends/creation.js +27 -0
- package/src/actions/guides/legends/edit.js +198 -66
- package/src/actions/guides/legends/focused.js +2 -2
- package/src/actions/guides/legends/lane.js +98 -86
- package/src/actions/guides/legends/lifecycle.js +132 -0
- package/src/actions/guides/legends/remove.js +31 -76
- package/src/actions/guides/legends/size.js +149 -105
- package/src/actions/guides/legends/strokeWidth.js +106 -64
- package/src/actions/guides/legends/target.js +12 -0
- package/src/actions/guides/legends/transition.js +43 -0
- package/src/actions/guides/polar/axes/facade.js +77 -62
- package/src/actions/guides/polar/axes/labels.js +5 -4
- package/src/actions/guides/polar/axes/lines.js +2 -1
- package/src/actions/guides/polar/axes/shared.js +6 -0
- package/src/actions/guides/polar/axes/ticks.js +5 -4
- package/src/actions/guides/polar/axes/titles.js +5 -4
- package/src/actions/guides/polar/grids.js +7 -7
- package/src/actions/guides/reuse.js +44 -0
- package/src/actions/guides/tickValues.js +19 -21
- package/src/actions/index.js +2 -0
- package/src/actions/intervals/resolve.js +35 -15
- package/src/actions/marks/arc/actions.js +1 -1
- package/src/actions/marks/area/actions.js +18 -4
- package/src/actions/marks/area/materialize.js +5 -2
- package/src/actions/marks/bar/materialize.js +11 -7
- package/src/actions/marks/index.js +5 -0
- package/src/actions/marks/line/actions.js +43 -108
- package/src/actions/marks/line/materialize.js +34 -52
- package/src/actions/marks/point/edit.js +11 -15
- package/src/actions/marks/point/index.js +1 -0
- package/src/actions/marks/point/jitter.js +3 -0
- package/src/actions/marks/point/materialize.js +138 -14
- package/src/actions/marks/point/packing.js +68 -0
- package/src/actions/marks/references.js +87 -0
- package/src/actions/marks/remove.js +17 -4
- package/src/actions/marks/rule/actions.js +60 -4
- package/src/actions/marks/rule/appearance.js +26 -0
- package/src/actions/marks/shared.js +12 -12
- package/src/actions/marks/text/actions.js +224 -6
- package/src/actions/marks/text/layout.js +2 -2
- package/src/actions/primitives/createGraphics.js +3 -1
- package/src/actions/primitives/semantic.js +1 -1
- package/src/actions/primitives/semanticAction.js +39 -2
- package/src/actions/primitives/semanticValidation/guide.js +10 -0
- package/src/actions/primitives/semanticValidation/layer.js +18 -1
- package/src/actions/primitives/semanticValidation/scale.js +10 -1
- package/src/actions/regression/create.js +1 -1
- package/src/actions/regression/edit.js +19 -6
- package/src/actions/regression/resolve.js +1 -1
- package/src/actions/scales/consumers/common.js +20 -53
- package/src/actions/scales/consumers/families.js +27 -14
- package/src/actions/scales/consumers/index.js +23 -2
- package/src/actions/scales/consumers/seriesLayout.js +33 -2
- package/src/actions/scales/create.js +4 -4
- package/src/actions/scales/definitions.js +19 -3
- package/src/actions/scales/edit.js +46 -34
- package/src/actions/scales/editPolicy.js +21 -31
- package/src/actions/scales/materialize.js +10 -46
- package/src/actions/scales/patch.js +1 -1
- package/src/actions/scales/preview.js +39 -0
- package/src/actions/scales/quantitativeColor.js +9 -5
- package/src/actions/theme/actions.js +43 -0
- package/src/actions/theme/index.js +9 -0
- package/src/actions/theme/reconcile.js +882 -0
- package/src/actions/titles/actions.js +11 -10
- package/src/actions/titles/resolve.js +1 -40
- package/src/actions/violinPlots/create.js +86 -42
- package/src/actions/violinPlots/edit.js +191 -0
- package/src/actions/violinPlots/index.js +2 -0
- package/src/core/ChartProgram.js +7 -0
- package/src/core/action.js +22 -2
- package/src/core/compositionState.js +136 -8
- package/src/core/vocabulary.js +2 -2
- package/src/grammar/aggregate.js +66 -23
- package/src/grammar/arcs.js +213 -65
- package/src/grammar/areaEndpoints.js +46 -0
- package/src/grammar/areaSeries.js +87 -41
- package/src/grammar/bars/aggregate.js +33 -12
- package/src/grammar/bars/policy.js +9 -7
- package/src/grammar/bin.js +181 -0
- package/src/grammar/categoryOrder.js +34 -0
- package/src/grammar/computed.js +150 -0
- package/src/grammar/ecdf.js +198 -0
- package/src/grammar/facets/index.js +117 -6
- package/src/grammar/fold.js +123 -0
- package/src/grammar/horizon.js +6 -4
- package/src/grammar/interval.js +37 -20
- package/src/grammar/lineSeries.js +20 -63
- package/src/grammar/markFilter.js +22 -4
- package/src/grammar/markLabels.js +93 -0
- package/src/grammar/parallelAxisTitles.js +16 -0
- package/src/grammar/pathSeries.js +91 -0
- package/src/grammar/pointPacking.js +183 -0
- package/src/grammar/positionCompatibility.js +1 -1
- package/src/grammar/positionDatum.js +12 -0
- package/src/grammar/rects.js +9 -1
- package/src/grammar/regression/derive.js +4 -0
- package/src/grammar/regression/index.js +7 -2
- package/src/grammar/regression/models.js +22 -7
- package/src/grammar/regression/parameters.js +40 -13
- package/src/grammar/rotation.js +29 -0
- package/src/grammar/rules.js +9 -21
- package/src/grammar/scales/color.js +23 -1
- package/src/grammar/scales/colorConsumers.js +38 -0
- package/src/grammar/scales/definition.js +15 -1
- package/src/grammar/scales/discretized.js +11 -5
- package/src/grammar/scales/fields.js +38 -8
- package/src/grammar/scales/mapping.js +2 -0
- package/src/grammar/scales/ordinal.js +23 -0
- package/src/grammar/scales/radial.js +55 -0
- package/src/grammar/scales/types.js +1 -0
- package/src/grammar/schemas/semanticPath.js +17 -3
- package/src/grammar/stack.js +170 -0
- package/src/grammar/statistics/confidenceInterval.js +101 -0
- package/src/grammar/summary.js +183 -0
- package/src/grammar/text.js +22 -16
- package/src/grammar/timeUnit.js +21 -11
- package/src/grammar/transformTopology.js +6 -0
- package/src/grammar/transforms.js +36 -0
- package/src/grammar/valueFormat.js +112 -0
- package/src/grammar/window.js +13 -7
- package/src/layout/facets.js +86 -23
- package/src/layout/guideCollisions.js +12 -0
- package/src/layout/legend.js +2 -10
- package/src/layout/legendItems.js +102 -0
- package/src/layout/legendLane.js +100 -11
- package/src/materialization/bars/aggregate.js +38 -152
- package/src/materialization/bars/histogram.js +24 -0
- package/src/materialization/categorySlotOffset.js +40 -0
- package/src/materialization/compositionSnapshot.js +1 -1
- package/src/materialization/dependencies.js +57 -16
- package/src/materialization/encodings.js +1 -1
- package/src/materialization/facetGuides/legacyCategorical.js +7 -1
- package/src/materialization/facetGuides/placement.js +16 -1
- package/src/materialization/facetGuides/preparation.js +26 -1
- package/src/materialization/facets.js +11 -2
- package/src/materialization/guides/layout.js +57 -0
- package/src/materialization/guides/resources.js +2 -2
- package/src/materialization/legends.js +10 -1
- package/src/materialization/marks/capabilities.js +30 -7
- package/src/materialization/marks/grouping.js +52 -0
- package/src/materialization/marks/index.js +63 -25
- package/src/materialization/marks/policies.js +2 -0
- package/src/materialization/rect.js +33 -33
- package/src/materialization/rowEncoding.js +70 -14
- package/src/materialization/scaleGuideDependencies.js +8 -5
- package/src/materialization/scales/map.js +1 -0
- package/src/materialization/scales/policies/arc.js +42 -0
- package/src/materialization/scales/policies/bar.js +0 -43
- package/src/materialization/scales/policies/binnedPosition.js +7 -4
- package/src/materialization/scales/policies/offset.js +54 -0
- package/src/materialization/scales/policies/series.js +2 -1
- package/src/materialization/scales/resolve.js +48 -18
- package/src/materialization/selection/compatibility.js +11 -0
- package/src/materialization/selection/items/bar.js +12 -68
- package/src/materialization/selection/items/common.js +8 -2
- package/src/materialization/selection/items/path.js +3 -13
- package/src/materialization/selection/items/rect.js +7 -4
- package/src/materialization/text.js +64 -5
- package/src/mcp/adapter.js +2 -2
- package/src/selectors/datasets.js +13 -0
- package/src/selectors/layers.js +3 -2
- package/src/selectors/scales.js +6 -0
- package/src/theme/defaults.js +39 -0
- package/types/basic.d.ts +11 -2
- package/types/index.d.ts +126 -0
- package/types/program.d.ts +1327 -124
- package/src/materialization/bars/grouped.js +0 -144
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import { stackLayoutMode } from "../seriesLayout.js";
|
|
2
|
+
import { resolveBarGrain } from "../../../grammar/bars/policy.js";
|
|
1
3
|
import { action } from "../../../core/action.js";
|
|
2
4
|
import {
|
|
3
5
|
getPositionEncodingMaterializationSteps
|
|
4
6
|
} from "../../../materialization/marks/index.js";
|
|
5
7
|
import { resolvePositionEncoding } from "./resolve.js";
|
|
6
8
|
import { findLayer } from "../../../selectors/layers.js";
|
|
9
|
+
import { findSemanticScale } from "../../../selectors/scales.js";
|
|
7
10
|
import { applyPositionSemantics } from "./apply.js";
|
|
8
11
|
import {
|
|
9
12
|
applyEncodingScale,
|
|
13
|
+
applyDetachedScaleRematerialization,
|
|
10
14
|
rebindPositionGuides
|
|
11
15
|
} from "../shared.js";
|
|
12
16
|
|
|
@@ -20,12 +24,14 @@ function encodePosition(program, channel, args, operation) {
|
|
|
20
24
|
datum,
|
|
21
25
|
hasField,
|
|
22
26
|
fieldType,
|
|
27
|
+
temporalUnit,
|
|
23
28
|
scale,
|
|
24
29
|
coordinate,
|
|
25
30
|
bin,
|
|
26
31
|
aggregate,
|
|
27
32
|
stack,
|
|
28
|
-
weight
|
|
33
|
+
weight,
|
|
34
|
+
companion
|
|
29
35
|
} = resolvePositionEncoding(program, channel, args, operation);
|
|
30
36
|
|
|
31
37
|
let next = program
|
|
@@ -43,6 +49,7 @@ function encodePosition(program, channel, args, operation) {
|
|
|
43
49
|
datum,
|
|
44
50
|
hasField,
|
|
45
51
|
fieldType,
|
|
52
|
+
temporalUnit,
|
|
46
53
|
bin,
|
|
47
54
|
aggregate,
|
|
48
55
|
stack,
|
|
@@ -54,8 +61,12 @@ function encodePosition(program, channel, args, operation) {
|
|
|
54
61
|
value: scale.id
|
|
55
62
|
});
|
|
56
63
|
next = applyEncodingScale(next, scale, requestedScale, {
|
|
57
|
-
reassignment: previous?.scale === scale.id
|
|
64
|
+
reassignment: previous?.scale === scale.id,
|
|
65
|
+
allowTypeChange: layer.mark.type === "area"
|
|
58
66
|
});
|
|
67
|
+
if (["bar", "area"].includes(layer.mark.type) && Object.hasOwn(args, "stack") && args.stack !== undefined) {
|
|
68
|
+
next = next.editSemantic({ property: `layer[${target}].layout.mode`, value: stackLayoutMode(args.stack) });
|
|
69
|
+
}
|
|
59
70
|
next = rebindPositionGuides(
|
|
60
71
|
next,
|
|
61
72
|
channel,
|
|
@@ -64,13 +75,30 @@ function encodePosition(program, channel, args, operation) {
|
|
|
64
75
|
target
|
|
65
76
|
);
|
|
66
77
|
|
|
78
|
+
if (companion !== undefined) {
|
|
79
|
+
const encoding = companion.encoding;
|
|
80
|
+
const storedScale = findSemanticScale(next, encoding.scale);
|
|
81
|
+
next = next[companion.channel === "x" ? "encodeX" : "encodeY"]({
|
|
82
|
+
target,
|
|
83
|
+
field: encoding.field,
|
|
84
|
+
fieldType: encoding.fieldType,
|
|
85
|
+
aggregate: encoding.aggregate,
|
|
86
|
+
...(layer.layout?.mode === undefined ? {} : { stack: encoding.stack }),
|
|
87
|
+
scale: {
|
|
88
|
+
id: encoding.scale,
|
|
89
|
+
...(storedScale?.zero === undefined ? { zero: encoding.stack !== null } : {})
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
67
94
|
if (layer.mark.type === "bar") {
|
|
68
95
|
const updated = findLayer(next, target);
|
|
69
96
|
const pendingBox = next.markConfigs[target]?.boxPlot;
|
|
70
97
|
if (pendingBox !== undefined && !pendingBox.materialized) {
|
|
71
|
-
|
|
98
|
+
const materialized = updated.encoding?.x !== undefined && updated.encoding?.y !== undefined
|
|
72
99
|
? next.materializeBoxPlot({ id: target })
|
|
73
100
|
: next;
|
|
101
|
+
return applyDetachedScaleRematerialization(materialized, [layer]);
|
|
74
102
|
}
|
|
75
103
|
}
|
|
76
104
|
|
|
@@ -78,13 +106,23 @@ function encodePosition(program, channel, args, operation) {
|
|
|
78
106
|
const updated = findLayer(next, target);
|
|
79
107
|
const pendingGradient = next.markConfigs[target]?.gradientPlot;
|
|
80
108
|
if (pendingGradient !== undefined && !pendingGradient.materialized) {
|
|
81
|
-
|
|
109
|
+
const materialized = updated.encoding?.x !== undefined && updated.encoding?.y !== undefined
|
|
82
110
|
? next.materializeGradientPlot({ id: target })
|
|
83
111
|
: next;
|
|
112
|
+
return applyDetachedScaleRematerialization(materialized, [layer]);
|
|
84
113
|
}
|
|
85
114
|
}
|
|
86
115
|
|
|
87
116
|
const updated = findLayer(next, target);
|
|
117
|
+
if (["bar", "area"].includes(layer.mark.type) && next.markConfigs[target]?.boxPlot === undefined &&
|
|
118
|
+
updated.encoding?.x?.scale !== undefined && updated.encoding?.y?.scale !== undefined) {
|
|
119
|
+
const requested = Object.hasOwn(args, "stack") ? args.stack : undefined;
|
|
120
|
+
const mode = requested !== undefined ? stackLayoutMode(requested) : updated.layout?.mode ??
|
|
121
|
+
(layer.mark.type === "bar" && resolveBarGrain(updated) === "histogram" ? stackLayoutMode(updated.encoding.y.stack) : undefined);
|
|
122
|
+
if (mode !== undefined && (layer.mark.type === "area" || resolveBarGrain(updated) !== undefined)) {
|
|
123
|
+
return applyDetachedScaleRematerialization(next.layoutSeries({ target, mode }), [layer]);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
88
126
|
for (const step of getPositionEncodingMaterializationSteps(
|
|
89
127
|
next,
|
|
90
128
|
updated,
|
|
@@ -92,7 +130,7 @@ function encodePosition(program, channel, args, operation) {
|
|
|
92
130
|
)) {
|
|
93
131
|
next = next[step.op](step.args);
|
|
94
132
|
}
|
|
95
|
-
return next;
|
|
133
|
+
return applyDetachedScaleRematerialization(next, [layer]);
|
|
96
134
|
}
|
|
97
135
|
|
|
98
136
|
const encodeX = action(
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { validateRadialMapping } from "../../../../grammar/scales/radial.js";
|
|
1
2
|
import { validateAggregate } from "../../../../grammar/aggregate.js";
|
|
2
3
|
import { validateNonEmptyString } from "../../../../core/validation.js";
|
|
3
4
|
import { emptyPositionPolicy } from "./common.js";
|
|
4
5
|
|
|
5
|
-
export function resolveArcPositionPolicy({ channel, args, fieldType }) {
|
|
6
|
+
export function resolveArcPositionPolicy({ channel, args, fieldType, layer }) {
|
|
6
7
|
if (args.bin !== undefined) {
|
|
7
8
|
throw new Error(`Arc ${channel} encoding does not support bin.`);
|
|
8
9
|
}
|
|
@@ -10,6 +11,24 @@ export function resolveArcPositionPolicy({ channel, args, fieldType }) {
|
|
|
10
11
|
throw new Error(`Arc ${channel} encoding does not support stack.`);
|
|
11
12
|
}
|
|
12
13
|
if (channel === "radius") {
|
|
14
|
+
if (layer.encoding?.theta?.fieldType === "quantitative") {
|
|
15
|
+
throw new Error(
|
|
16
|
+
"Arc radius encoding cannot be combined with quantitative theta."
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
if (args.mapping !== undefined) {
|
|
20
|
+
validateRadialMapping(args.mapping);
|
|
21
|
+
if (fieldType !== "quantitative" || !["count", "sum"].includes(args.aggregate)) {
|
|
22
|
+
throw new Error("Measured radius requires quantitative count or sum aggregation.");
|
|
23
|
+
}
|
|
24
|
+
if (args.aggregate === "count" && Object.hasOwn(args, "field")) {
|
|
25
|
+
throw new Error("Measured radius count does not accept a field.");
|
|
26
|
+
}
|
|
27
|
+
if (layer.encoding?.theta?.aggregate !== undefined) {
|
|
28
|
+
throw new Error("Measured radius requires equal-angle theta without aggregation.");
|
|
29
|
+
}
|
|
30
|
+
return { ...emptyPositionPolicy(), aggregate: args.aggregate };
|
|
31
|
+
}
|
|
13
32
|
if (args.aggregate !== undefined) {
|
|
14
33
|
throw new Error("Arc radius encoding does not support aggregate.");
|
|
15
34
|
}
|
|
@@ -18,8 +37,24 @@ export function resolveArcPositionPolicy({ channel, args, fieldType }) {
|
|
|
18
37
|
}
|
|
19
38
|
return emptyPositionPolicy();
|
|
20
39
|
}
|
|
40
|
+
if (fieldType === "quantitative") {
|
|
41
|
+
if (layer.encoding?.radius !== undefined) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
"Quantitative arc theta cannot be combined with radius encoding."
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
if (args.aggregate !== undefined) {
|
|
47
|
+
throw new Error("Quantitative arc theta does not support aggregate.");
|
|
48
|
+
}
|
|
49
|
+
if (args.weight !== undefined) {
|
|
50
|
+
throw new Error("Quantitative arc theta does not support weight.");
|
|
51
|
+
}
|
|
52
|
+
return emptyPositionPolicy();
|
|
53
|
+
}
|
|
21
54
|
if (!["ordinal", "nominal"].includes(fieldType)) {
|
|
22
|
-
throw new Error(
|
|
55
|
+
throw new Error(
|
|
56
|
+
"Arc theta encoding requires a quantitative, ordinal, or nominal field."
|
|
57
|
+
);
|
|
23
58
|
}
|
|
24
59
|
if (args.aggregate === undefined) {
|
|
25
60
|
if (args.weight !== undefined) {
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { validateStack, emptyPositionPolicy } from "./common.js";
|
|
2
|
-
import { deriveCenteredAreaSeries } from "../../../../grammar/areaSeries.js";
|
|
3
|
-
import { readQuantitativeField } from "../../../../grammar/scales/index.js";
|
|
4
2
|
|
|
5
3
|
export function isCategoricalDensityPosition({
|
|
6
4
|
layer,
|
|
@@ -50,44 +48,8 @@ export function resolveAreaPositionPolicy({
|
|
|
50
48
|
throw new Error("Area position encoding does not support aggregate or bin.");
|
|
51
49
|
}
|
|
52
50
|
if (args.stack === undefined) return emptyPositionPolicy();
|
|
53
|
-
const stack = validateStack(args.stack, "Area
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
if (layer.encoding?.group?.fieldType !== "nominal") {
|
|
59
|
-
throw new Error("Area center stack requires a nominal group encoding.");
|
|
60
|
-
}
|
|
61
|
-
if (
|
|
62
|
-
layer.encoding?.color?.layout !== undefined &&
|
|
63
|
-
layer.encoding.color.layout !== "center"
|
|
64
|
-
) {
|
|
65
|
-
throw new Error("Area center stack requires a matching center color layout.");
|
|
66
|
-
}
|
|
67
|
-
const values = readQuantitativeField(dataset.values, field);
|
|
68
|
-
if (values.some(value => value < 0)) {
|
|
69
|
-
throw new RangeError("Area center stack requires non-negative values.");
|
|
70
|
-
}
|
|
71
|
-
if (layer.encoding?.x !== undefined) {
|
|
72
|
-
deriveCenteredAreaSeries(dataset.values, {
|
|
73
|
-
...layer,
|
|
74
|
-
encoding: {
|
|
75
|
-
...layer.encoding,
|
|
76
|
-
y: { field, fieldType, stack: "center" }
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
return { bin: undefined, aggregate: undefined, stack };
|
|
81
|
-
}
|
|
82
|
-
if (
|
|
83
|
-
channel !== "y" ||
|
|
84
|
-
density === undefined
|
|
85
|
-
) {
|
|
86
|
-
throw new Error("Area stack currently requires a density y encoding.");
|
|
87
|
-
}
|
|
88
|
-
return {
|
|
89
|
-
bin: undefined,
|
|
90
|
-
aggregate: undefined,
|
|
91
|
-
stack
|
|
92
|
-
};
|
|
51
|
+
const stack = validateStack(args.stack, "Area position encoding");
|
|
52
|
+
if (fieldType !== "quantitative") throw new Error("Area stack requires a quantitative measure.");
|
|
53
|
+
if (stack === "center" && channel !== "y") throw new Error("Area center stack requires a y encoding.");
|
|
54
|
+
return { bin: undefined, aggregate: undefined, stack };
|
|
93
55
|
}
|
|
@@ -4,12 +4,14 @@ import {
|
|
|
4
4
|
} from "../../../../grammar/aggregate.js";
|
|
5
5
|
import {
|
|
6
6
|
BAR_ORIENTATIONS,
|
|
7
|
+
BAR_GRAINS,
|
|
8
|
+
resolveBarGrain,
|
|
7
9
|
resolveBarOffsetChannel,
|
|
8
10
|
resolveBarOrientation
|
|
9
11
|
} from "../../../../grammar/bars/policy.js";
|
|
10
12
|
import { resolveBin, validateStack } from "./common.js";
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
function resolveBarChannelPolicy({
|
|
13
15
|
program,
|
|
14
16
|
layer,
|
|
15
17
|
channel,
|
|
@@ -72,17 +74,12 @@ export function resolveBarPositionPolicy({
|
|
|
72
74
|
if (pendingBoxRange && args.aggregate === undefined) {
|
|
73
75
|
return { bin, aggregate, stack };
|
|
74
76
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
: "Bar y encoding requires a binned quantitative or ordinal x category, temporal x category, or aggregate."
|
|
80
|
-
);
|
|
77
|
+
stack = Object.hasOwn(args, "stack") ? args.stack : opposite === undefined ? undefined : null;
|
|
78
|
+
if (aggregate !== undefined) {
|
|
79
|
+
aggregate = validateAggregate(aggregate);
|
|
80
|
+
validateAggregateFieldType(aggregate, fieldType);
|
|
81
81
|
}
|
|
82
|
-
stack
|
|
83
|
-
aggregate = validateAggregate(aggregate);
|
|
84
|
-
validateAggregateFieldType(aggregate, fieldType);
|
|
85
|
-
stack = validateStack(stack, `Bar ${channel} encoding`);
|
|
82
|
+
if (stack !== undefined) stack = validateStack(stack, `Bar ${channel} encoding`);
|
|
86
83
|
if (stack === "center") {
|
|
87
84
|
throw new Error("Centered bars are not supported.");
|
|
88
85
|
}
|
|
@@ -92,13 +89,45 @@ export function resolveBarPositionPolicy({
|
|
|
92
89
|
);
|
|
93
90
|
}
|
|
94
91
|
|
|
92
|
+
return { bin, aggregate, stack };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function resolveBarPositionPolicy(context) {
|
|
96
|
+
const { program, layer, channel, field, fieldType } = context;
|
|
97
|
+
const policy = resolveBarChannelPolicy(context);
|
|
98
|
+
const oppositeChannel = channel === "x" ? "y" : "x";
|
|
99
|
+
const opposite = layer.encoding?.[oppositeChannel];
|
|
100
|
+
const pendingBoxRange = program.markConfigs[layer.id]?.boxPlot !== undefined;
|
|
95
101
|
const candidate = {
|
|
96
102
|
...layer,
|
|
97
103
|
encoding: {
|
|
98
104
|
...layer.encoding,
|
|
99
|
-
[channel]: { field, fieldType,
|
|
105
|
+
[channel]: { field, fieldType, ...policy }
|
|
100
106
|
}
|
|
101
107
|
};
|
|
108
|
+
// The same channel policy owns a measure whether it is authored first or last.
|
|
109
|
+
// Absence of an aggregate/stack is sufficient to represent an unresolved role.
|
|
110
|
+
let companion;
|
|
111
|
+
if (
|
|
112
|
+
!pendingBoxRange && resolveBarGrain(layer) === undefined &&
|
|
113
|
+
opposite?.fieldType === "quantitative" && opposite.bin === undefined
|
|
114
|
+
) {
|
|
115
|
+
const resolved = resolveBarChannelPolicy({
|
|
116
|
+
...context,
|
|
117
|
+
layer: candidate,
|
|
118
|
+
channel: oppositeChannel,
|
|
119
|
+
field: opposite.field,
|
|
120
|
+
fieldType: opposite.fieldType,
|
|
121
|
+
args: opposite
|
|
122
|
+
});
|
|
123
|
+
candidate.encoding[oppositeChannel] = { ...opposite, ...resolved };
|
|
124
|
+
if (
|
|
125
|
+
(opposite.aggregate === undefined && resolved.aggregate !== undefined) ||
|
|
126
|
+
opposite.stack !== resolved.stack
|
|
127
|
+
) {
|
|
128
|
+
companion = { channel: oppositeChannel, encoding: candidate.encoding[oppositeChannel] };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
102
131
|
const orientation = resolveBarOrientation(candidate);
|
|
103
132
|
if (opposite !== undefined && orientation === undefined && !pendingBoxRange) {
|
|
104
133
|
throw new Error(
|
|
@@ -116,5 +145,11 @@ export function resolveBarPositionPolicy({
|
|
|
116
145
|
`${orientation} bars require ${expectedOffset}; remove the incompatible ${incompatibleOffset} encoding.`
|
|
117
146
|
);
|
|
118
147
|
}
|
|
119
|
-
|
|
148
|
+
if (
|
|
149
|
+
program.markConfigs[layer.id]?.barWidth !== undefined &&
|
|
150
|
+
resolveBarGrain(candidate) === BAR_GRAINS.histogram
|
|
151
|
+
) {
|
|
152
|
+
throw new Error("A saved Bar width requires an aggregate or ranged category slot and cannot be applied to histogram bins.");
|
|
153
|
+
}
|
|
154
|
+
return companion === undefined ? policy : { ...policy, companion };
|
|
120
155
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { isSourceOwnedText } from "../../../grammar/text.js";
|
|
2
|
+
import { findSemanticScale } from "../../../selectors/scales.js";
|
|
3
|
+
import { readAreaEndpoint } from "../../../grammar/areaEndpoints.js";
|
|
1
4
|
import { validateUserId } from "../../../core/identifiers.js";
|
|
2
5
|
import { getPositionCoordinateDefaults } from "../../../grammar/coordinates.js";
|
|
3
6
|
import {
|
|
@@ -6,6 +9,7 @@ import {
|
|
|
6
9
|
readQuantitativeField,
|
|
7
10
|
readScaleField,
|
|
8
11
|
readTemporalField,
|
|
12
|
+
resolveTemporalUnit,
|
|
9
13
|
validateFieldType,
|
|
10
14
|
validatePositionChannel
|
|
11
15
|
} from "../../../grammar/scales/index.js";
|
|
@@ -21,8 +25,11 @@ import {
|
|
|
21
25
|
validateAggregateFieldType,
|
|
22
26
|
validateAggregateFieldValues,
|
|
23
27
|
} from "../../../grammar/aggregate.js";
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
28
|
+
import { normalizePositionDatum } from "../../../grammar/positionDatum.js";
|
|
29
|
+
import {
|
|
30
|
+
readArcThetaValues,
|
|
31
|
+
readArcThetaWeights
|
|
32
|
+
} from "../../../grammar/arcs.js";
|
|
26
33
|
import { resolveMarkPositionPolicy } from "./policies/index.js";
|
|
27
34
|
import {
|
|
28
35
|
getPositionChannelDefinition,
|
|
@@ -31,10 +38,10 @@ import {
|
|
|
31
38
|
|
|
32
39
|
const POSITION_ENCODING_OPTIONS = Object.freeze([
|
|
33
40
|
"field", "datum", "target", "fieldType", "scale", "coordinate",
|
|
34
|
-
"aggregate", "bin", "stack", "weight"
|
|
41
|
+
"aggregate", "bin", "stack", "weight", "temporalUnit", "mapping"
|
|
35
42
|
]);
|
|
36
43
|
|
|
37
|
-
function
|
|
44
|
+
function inferDatumFieldType(datum, operation) {
|
|
38
45
|
if (Number.isFinite(datum)) return "quantitative";
|
|
39
46
|
if (isNominalValue(datum)) return "nominal";
|
|
40
47
|
throw new Error(`${operation} datum requires an explicit fieldType.`);
|
|
@@ -90,29 +97,45 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
90
97
|
args.target,
|
|
91
98
|
getPositionChannelDefinition(channel).markTypes
|
|
92
99
|
);
|
|
100
|
+
if (isSourceOwnedText(layer)) {
|
|
101
|
+
throw new Error(`${operation} cannot replace source-owned Text positions; edit the source, use dx/dy, or create independent Text with data.`);
|
|
102
|
+
}
|
|
93
103
|
if (
|
|
94
104
|
Object.hasOwn(args, "weight") &&
|
|
95
105
|
!(layer.mark.type === "arc" && channel === "theta")
|
|
96
106
|
) {
|
|
97
107
|
throw new Error(`${operation} weight is supported only for arc theta encoding.`);
|
|
98
108
|
}
|
|
109
|
+
if (Object.hasOwn(args, "mapping") && (layer.mark.type !== "arc" || channel !== "radius")) {
|
|
110
|
+
throw new Error(`${operation} mapping requires Arc radius.`);
|
|
111
|
+
}
|
|
99
112
|
validateCoordinateFamily(layer, channel, operation);
|
|
100
113
|
const hasField = Object.hasOwn(args, "field");
|
|
101
114
|
const hasDatum = Object.hasOwn(args, "datum");
|
|
102
|
-
if (layer.mark.type
|
|
115
|
+
if (["rule", "rect", "text", "point", "tick"].includes(layer.mark.type)) {
|
|
103
116
|
if (hasField === hasDatum) {
|
|
104
|
-
throw new Error(`${operation} requires exactly one of field or datum for a
|
|
117
|
+
throw new Error(`${operation} requires exactly one of field or datum for a ${layer.mark.type} mark.`);
|
|
105
118
|
}
|
|
106
|
-
if (hasField && args.fieldType === undefined) {
|
|
119
|
+
if (layer.mark.type === "rule" && hasField && args.fieldType === undefined) {
|
|
107
120
|
throw new Error(`${operation} requires fieldType for a rule mark.`);
|
|
108
121
|
}
|
|
122
|
+
} else if (layer.mark.type === "area") {
|
|
123
|
+
if (hasField === hasDatum) throw new Error(`${operation} requires exactly one of field or datum for an area mark.`);
|
|
124
|
+
if (hasDatum && (!Number.isFinite(args.datum) || (args.fieldType ?? "quantitative") !== "quantitative" ||
|
|
125
|
+
["aggregate", "bin", "stack", "temporalUnit"].some(key => Object.hasOwn(args, key)))) {
|
|
126
|
+
throw new Error("Area datum requires a finite quantitative constant without aggregate, bin, stack, or temporalUnit.");
|
|
127
|
+
}
|
|
109
128
|
} else if (hasDatum) {
|
|
110
129
|
throw new Error(`${operation} does not support datum for a ${layer.mark.type} mark.`);
|
|
111
130
|
}
|
|
112
131
|
const previous = layer.encoding?.[channel];
|
|
132
|
+
const mapping = channel === "radius" && layer.mark.type === "arc"
|
|
133
|
+
? args.mapping ?? findSemanticScale(program, previous?.scale)?.radialMapping
|
|
134
|
+
: undefined;
|
|
135
|
+
const countRadius = mapping !== undefined && (args.aggregate ?? previous?.aggregate) === "count";
|
|
113
136
|
const requestedFieldType = args.fieldType ?? previous?.fieldType ?? (
|
|
114
|
-
layer.mark.type
|
|
115
|
-
?
|
|
137
|
+
["rule", "rect", "text", "point", "tick"].includes(layer.mark.type) && hasDatum
|
|
138
|
+
? inferDatumFieldType(args.datum, operation)
|
|
116
139
|
: layer.mark.type === "arc" && channel === "theta" &&
|
|
117
140
|
["count", "sum"].includes(args.aggregate)
|
|
118
141
|
? "nominal"
|
|
@@ -127,10 +150,30 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
127
150
|
? xEncoding.field
|
|
128
151
|
: args.field;
|
|
129
152
|
const datum = args.datum;
|
|
130
|
-
const
|
|
153
|
+
const temporalUnit = resolveTemporalUnit({ ...args, ...(hasDatum ? {} : { field }) }, fieldType, previous);
|
|
154
|
+
const usesField = !countRadius && (!["rule", "area", "rect", "text", "point", "tick"].includes(layer.mark.type) || hasField);
|
|
155
|
+
if (usesField && (typeof field !== "string" || field.length === 0)) {
|
|
156
|
+
throw new TypeError(`${operation} field must be a non-empty string.`);
|
|
157
|
+
}
|
|
158
|
+
// A missing field is invalid even before the Bar's measure/category role is known.
|
|
159
|
+
if (layer.mark.type === "bar" && program.markConfigs[target]?.boxPlot === undefined) {
|
|
160
|
+
if (fieldType === "quantitative" && dataset.values.length > 0 &&
|
|
161
|
+
!dataset.values.some(row => Object.hasOwn(row, field))) {
|
|
162
|
+
throw new Error(`${operation} field "${field}" does not exist in the dataset.`);
|
|
163
|
+
}
|
|
164
|
+
if (fieldType === "quantitative") {
|
|
165
|
+
validateAggregateFieldValues(dataset.values, field, fieldType);
|
|
166
|
+
} else readScaleField(dataset.values, field, fieldType, { temporalUnit });
|
|
167
|
+
}
|
|
168
|
+
const effectiveArgs = { ...args, ...(mapping === undefined ? {} : { mapping }) };
|
|
169
|
+
const directQuantitativeArcTheta =
|
|
170
|
+
layer.mark.type === "arc" &&
|
|
171
|
+
channel === "theta" &&
|
|
172
|
+
fieldType === "quantitative";
|
|
131
173
|
for (const property of ["aggregate", "bin", "stack"]) {
|
|
132
174
|
if (!Object.hasOwn(effectiveArgs, property) && previous !== undefined &&
|
|
133
|
-
Object.hasOwn(previous, property)
|
|
175
|
+
Object.hasOwn(previous, property) &&
|
|
176
|
+
!(directQuantitativeArcTheta && property === "aggregate")) {
|
|
134
177
|
effectiveArgs[property] = previous[property];
|
|
135
178
|
}
|
|
136
179
|
}
|
|
@@ -151,11 +194,6 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
151
194
|
fieldType,
|
|
152
195
|
field
|
|
153
196
|
});
|
|
154
|
-
const usesField = layer.mark.type !== "rule" || hasField;
|
|
155
|
-
if (usesField && (typeof field !== "string" || field.length === 0)) {
|
|
156
|
-
throw new TypeError(`${operation} field must be a non-empty string.`);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
197
|
const aggregateOutput = isAggregate(policy.aggregate) && !(
|
|
160
198
|
layer.mark.type === "arc" && channel === "theta"
|
|
161
199
|
);
|
|
@@ -168,11 +206,14 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
168
206
|
channel,
|
|
169
207
|
aggregateOutput ? "quantitative" : fieldType,
|
|
170
208
|
requestedScale,
|
|
171
|
-
|
|
209
|
+
mapping !== undefined ? { radialMapping: mapping, zero: true, nice: false }
|
|
210
|
+
: layer.mark.type === "bar"
|
|
172
211
|
? program.markConfigs[target]?.boxPlot !== undefined && fieldType === "quantitative"
|
|
173
212
|
? { nice: true, zero: false }
|
|
174
213
|
: fieldType === "quantitative"
|
|
175
|
-
? policy.bin
|
|
214
|
+
? policy.bin === undefined && policy.stack === undefined
|
|
215
|
+
? { nice: true }
|
|
216
|
+
: policy.bin !== undefined || policy.stack === null
|
|
176
217
|
? { nice: true, zero: false }
|
|
177
218
|
: { nice: true, zero: true }
|
|
178
219
|
: fieldType === "temporal"
|
|
@@ -209,8 +250,13 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
209
250
|
"Position scale unknown currently requires a row-owned point mark."
|
|
210
251
|
);
|
|
211
252
|
}
|
|
212
|
-
if (layer.mark.type === "
|
|
213
|
-
|
|
253
|
+
if (layer.mark.type === "area" && fieldType === "quantitative") {
|
|
254
|
+
readAreaEndpoint(dataset.values, { ...(hasDatum ? { datum } : { field }), fieldType }, layer.mark.missing);
|
|
255
|
+
} else if (["rule", "rect", "text", "point", "tick"].includes(layer.mark.type) && hasDatum) {
|
|
256
|
+
const mark = layer.mark.type[0].toUpperCase() + layer.mark.type.slice(1);
|
|
257
|
+
normalizePositionDatum(datum, fieldType, channel, temporalUnit, mark);
|
|
258
|
+
} else if (countRadius) {
|
|
259
|
+
// Count has no source measure field. Category-grain validation runs once theta exists.
|
|
214
260
|
} else if (aggregateOutput) {
|
|
215
261
|
validateAggregateFieldType(policy.aggregate, fieldType);
|
|
216
262
|
validateAggregateFieldValues(dataset.values, field, fieldType);
|
|
@@ -220,6 +266,8 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
220
266
|
policy.aggregate === "sum"
|
|
221
267
|
) {
|
|
222
268
|
readArcThetaWeights(dataset.values, policy.weight, layer.id);
|
|
269
|
+
} else if (directQuantitativeArcTheta) {
|
|
270
|
+
readArcThetaValues(dataset.values, field, layer.id);
|
|
223
271
|
} else if (program.markConfigs[target]?.boxPlot !== undefined) {
|
|
224
272
|
for (const [index, row] of dataset.values.entries()) {
|
|
225
273
|
const value = row[field];
|
|
@@ -229,14 +277,20 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
229
277
|
}
|
|
230
278
|
}
|
|
231
279
|
} else if (layer.mark.type === "rect") {
|
|
232
|
-
readScaleField(dataset.values, field, fieldType, { allowUnknown: true });
|
|
280
|
+
readScaleField(dataset.values, field, fieldType, { allowUnknown: true, temporalUnit });
|
|
233
281
|
} else if (Object.hasOwn(scale, "unknown")) {
|
|
234
|
-
readScaleField(dataset.values, field, fieldType, { allowUnknown: true });
|
|
235
|
-
} else if (fieldType === "temporal") readTemporalField(dataset.values, field);
|
|
282
|
+
readScaleField(dataset.values, field, fieldType, { allowUnknown: true, temporalUnit });
|
|
283
|
+
} else if (fieldType === "temporal") readTemporalField(dataset.values, field, temporalUnit);
|
|
236
284
|
else if (["ordinal", "nominal"].includes(fieldType)) {
|
|
237
285
|
readNominalField(dataset.values, field);
|
|
238
286
|
} else readQuantitativeField(dataset.values, field);
|
|
239
287
|
|
|
288
|
+
if (mapping !== undefined) {
|
|
289
|
+
const values = countRadius ? dataset.values.map(() => 1) : readQuantitativeField(dataset.values, field);
|
|
290
|
+
if (values.length === 0 || values.some(value => value < 0) || !values.some(value => value > 0)) {
|
|
291
|
+
throw new Error("Measured radius requires non-negative values and a positive aggregate.");
|
|
292
|
+
}
|
|
293
|
+
}
|
|
240
294
|
if (
|
|
241
295
|
["bar", "rect"].includes(layer.mark.type) &&
|
|
242
296
|
["ordinal", "nominal"].includes(fieldType) &&
|
|
@@ -248,11 +302,12 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
248
302
|
target,
|
|
249
303
|
layer,
|
|
250
304
|
previous,
|
|
251
|
-
requestedScale,
|
|
305
|
+
requestedScale: mapping === undefined ? requestedScale : { ...requestedScale, radialMapping: mapping },
|
|
252
306
|
field,
|
|
253
307
|
datum,
|
|
254
308
|
hasField: usesField,
|
|
255
309
|
fieldType,
|
|
310
|
+
temporalUnit,
|
|
256
311
|
scale,
|
|
257
312
|
coordinate: resolveCoordinate(program, channel, layer, args.coordinate),
|
|
258
313
|
...policy
|