From da2ddbb901f765d4bfcf11fa438e9641083077d5 Mon Sep 17 00:00:00 2001 From: Kanit Wongsuphasawat Date: Mon, 9 Oct 2023 10:06:49 -0700 Subject: [PATCH] feat: make bar & rect marks support bandPosition (allow center-alignment with ticks) (#9126) * docs(examples): examples for using bandPosition=0 * chore: update examples [CI] * fix: calculate offseted rect position for timeUnit if bandPosition != 0.5 * fix: use offsetted rect position in aggregate * fix: use offsetted rect in mark position * fix: use offsetted rect in scale domain * fix: correctly read default bandPosition * fix: apply bandOffset to grouped bars with timeUnit * chore: update examples [CI] * style: auto-formatting [CI] * docs: update docs * chore: update TOC [CI] --------- Co-authored-by: GitHub Actions Bot --- ...r_binned_yearmonth_grouped_center_band.png | Bin 0 -> 11723 bytes ...r_binned_yearmonth_grouped_center_band.svg | 1 + ...nned_yearmonth_grouped_center_band.vg.json | 171 ++++++++++++++ ...bar_binned_yearmonth_label_band_center.png | Bin 0 -> 9995 bytes ...bar_binned_yearmonth_label_band_center.svg | 1 + ...binned_yearmonth_label_band_center.vg.json | 205 ++++++++++++++++ .../bar_month_temporal_band_center.png | Bin 0 -> 9999 bytes .../bar_month_temporal_band_center.svg | 1 + .../bar_month_temporal_band_center.vg.json | 160 +++++++++++++ .../bar_month_temporal_band_center_config.png | Bin 0 -> 9999 bytes .../bar_month_temporal_band_center_config.svg | 1 + ..._month_temporal_band_center_config.vg.json | 160 +++++++++++++ ...mple_binned_timeunit_special_chars.vg.json | 2 +- .../compiled/bar_yearmonth_center_band.png | Bin 0 -> 14127 bytes .../compiled/bar_yearmonth_center_band.svg | 1 + .../bar_yearmonth_center_band.vg.json | 163 +++++++++++++ ...t_heatmap_weather_temporal_center_band.png | Bin 0 -> 24755 bytes ...t_heatmap_weather_temporal_center_band.svg | 1 + ...atmap_weather_temporal_center_band.vg.json | 223 ++++++++++++++++++ ...ap_weather_temporal_center_band_config.png | Bin 0 -> 24755 bytes ...ap_weather_temporal_center_band_config.svg | 1 + ...eather_temporal_center_band_config.vg.json | 223 ++++++++++++++++++ .../compiled/time_parse_binnedutc.vg.json | 2 +- ...nned_yearmonth_grouped_center_band.vl.json | 15 ++ ...binned_yearmonth_label_band_center.vl.json | 21 ++ .../bar_month_temporal_band_center.vl.json | 9 + ..._month_temporal_band_center_config.vl.json | 12 + .../specs/bar_yearmonth_center_band.vl.json | 10 + ...month_label_band_center_normalized.vl.json | 42 ++++ ...atmap_weather_temporal_center_band.vl.json | 45 ++++ ...eather_temporal_center_band_config.vl.json | 46 ++++ site/_data/examples.json | 4 + site/_includes/docs_toc.md | 4 +- site/_layouts/docs.html | 4 +- site/docs/encoding/band.md | 21 -- site/docs/encoding/bandPosition.md | 31 +++ site/docs/transform/timeunit.md | 6 + src/channeldef.ts | 2 +- src/compile/data/aggregate.ts | 24 +- src/compile/data/timeunit.ts | 99 +++++++- src/compile/mark/encode/position-rect.ts | 27 ++- src/compile/mark/encode/valueref.ts | 4 +- src/compile/scale/domain.ts | 49 ++-- src/compile/scale/range.ts | 26 +- test/compile/data/aggregate.test.ts | 34 +++ test/compile/data/timeunit.test.ts | 91 ++++++- .../compile/mark/encode/position-rect.test.ts | 20 ++ test/compile/scale/domain.test.ts | 38 +++ test/compile/scale/range.test.ts | 27 ++- 49 files changed, 1947 insertions(+), 80 deletions(-) create mode 100644 examples/compiled/bar_binned_yearmonth_grouped_center_band.png create mode 100644 examples/compiled/bar_binned_yearmonth_grouped_center_band.svg create mode 100644 examples/compiled/bar_binned_yearmonth_grouped_center_band.vg.json create mode 100644 examples/compiled/bar_binned_yearmonth_label_band_center.png create mode 100644 examples/compiled/bar_binned_yearmonth_label_band_center.svg create mode 100644 examples/compiled/bar_binned_yearmonth_label_band_center.vg.json create mode 100644 examples/compiled/bar_month_temporal_band_center.png create mode 100644 examples/compiled/bar_month_temporal_band_center.svg create mode 100644 examples/compiled/bar_month_temporal_band_center.vg.json create mode 100644 examples/compiled/bar_month_temporal_band_center_config.png create mode 100644 examples/compiled/bar_month_temporal_band_center_config.svg create mode 100644 examples/compiled/bar_month_temporal_band_center_config.vg.json create mode 100644 examples/compiled/bar_yearmonth_center_band.png create mode 100644 examples/compiled/bar_yearmonth_center_band.svg create mode 100644 examples/compiled/bar_yearmonth_center_band.vg.json create mode 100644 examples/compiled/rect_heatmap_weather_temporal_center_band.png create mode 100644 examples/compiled/rect_heatmap_weather_temporal_center_band.svg create mode 100644 examples/compiled/rect_heatmap_weather_temporal_center_band.vg.json create mode 100644 examples/compiled/rect_heatmap_weather_temporal_center_band_config.png create mode 100644 examples/compiled/rect_heatmap_weather_temporal_center_band_config.svg create mode 100644 examples/compiled/rect_heatmap_weather_temporal_center_band_config.vg.json create mode 100644 examples/specs/bar_binned_yearmonth_grouped_center_band.vl.json create mode 100644 examples/specs/bar_binned_yearmonth_label_band_center.vl.json create mode 100644 examples/specs/bar_month_temporal_band_center.vl.json create mode 100644 examples/specs/bar_month_temporal_band_center_config.vl.json create mode 100644 examples/specs/bar_yearmonth_center_band.vl.json create mode 100644 examples/specs/normalized/bar_binned_yearmonth_label_band_center_normalized.vl.json create mode 100644 examples/specs/rect_heatmap_weather_temporal_center_band.vl.json create mode 100644 examples/specs/rect_heatmap_weather_temporal_center_band_config.vl.json delete mode 100644 site/docs/encoding/band.md create mode 100644 site/docs/encoding/bandPosition.md diff --git a/examples/compiled/bar_binned_yearmonth_grouped_center_band.png b/examples/compiled/bar_binned_yearmonth_grouped_center_band.png new file mode 100644 index 0000000000000000000000000000000000000000..c47310973c3a515eee91c968b728db678db3a6f5 GIT binary patch literal 11723 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 k#_)7c*3cjQz6vRJ@J$v=P061202LJ#7 literal 0 HcmV?d00001 diff --git a/examples/compiled/bar_binned_yearmonth_grouped_center_band.svg b/examples/compiled/bar_binned_yearmonth_grouped_center_band.svg new file mode 100644 index 00000000000..1d3814b5623 --- /dev/null +++ b/examples/compiled/bar_binned_yearmonth_grouped_center_band.svg @@ -0,0 +1 @@ +Jan 2005Feb 2005Mar 2005date050100150200priceAAPLAMZNGOOGIBMMSFTsymbol \ No newline at end of file diff --git a/examples/compiled/bar_binned_yearmonth_grouped_center_band.vg.json b/examples/compiled/bar_binned_yearmonth_grouped_center_band.vg.json new file mode 100644 index 00000000000..addd923f0b1 --- /dev/null +++ b/examples/compiled/bar_binned_yearmonth_grouped_center_band.vg.json @@ -0,0 +1,171 @@ +{ + "$schema": "https://vega.github.io/schema/vega/v5.json", + "description": "Stock price over time.", + "background": "white", + "padding": 5, + "width": 200, + "height": 200, + "style": "cell", + "data": [ + { + "name": "source_0", + "url": "data/stocks.csv", + "format": {"type": "csv", "parse": {"date": "date"}}, + "transform": [ + { + "type": "filter", + "expr": "inrange(time(datum[\"date\"]), [time(datetime(2005, 0, 1, 0, 0, 0, 0)), time(datetime(2005, 2, 1, 0, 0, 0, 0))])" + }, + { + "type": "formula", + "expr": "timeOffset('month', datum['date'], 1)", + "as": "date_end" + }, + { + "type": "formula", + "expr": "0.5 * timeOffset('month', datum['date'], -1) + 0.5 * datum['date']", + "as": "date_offsetted_rect_start" + }, + { + "type": "formula", + "expr": "0.5 * datum['date'] + 0.5 * datum['date_end']", + "as": "date_offsetted_rect_end" + }, + { + "type": "stack", + "groupby": ["date", "symbol"], + "field": "price", + "sort": {"field": [], "order": []}, + "as": ["price_start", "price_end"], + "offset": "zero" + }, + { + "type": "filter", + "expr": "(isDate(datum[\"date\"]) || (isValid(datum[\"date\"]) && isFinite(+datum[\"date\"]))) && isValid(datum[\"price\"]) && isFinite(+datum[\"price\"])" + } + ] + } + ], + "marks": [ + { + "name": "marks", + "type": "rect", + "style": ["bar"], + "from": {"data": "source_0"}, + "encode": { + "update": { + "fill": {"scale": "color", "field": "symbol"}, + "ariaRoleDescription": {"value": "bar"}, + "description": { + "signal": "\"date: \" + (timeFormat(datum[\"date\"], timeUnitSpecifier([\"year\",\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"}))) + \"; price: \" + (format(datum[\"price\"], \"\")) + \"; symbol: \" + (isValid(datum[\"symbol\"]) ? datum[\"symbol\"] : \"\"+datum[\"symbol\"])" + }, + "xc": { + "scale": "x", + "field": "date", + "offset": {"scale": "xOffset", "field": "symbol", "band": 0.5} + }, + "width": {"signal": "max(0.25, bandwidth('xOffset'))"}, + "y": {"scale": "y", "field": "price_end"}, + "y2": {"scale": "y", "field": "price_start"} + } + } + } + ], + "scales": [ + { + "name": "x", + "type": "time", + "domain": { + "data": "source_0", + "fields": ["date_offsetted_rect_start", "date_offsetted_rect_end"] + }, + "range": [0, {"signal": "width"}] + }, + { + "name": "y", + "type": "linear", + "domain": {"data": "source_0", "fields": ["price_start", "price_end"]}, + "range": [{"signal": "height"}, 0], + "nice": true, + "zero": true + }, + { + "name": "xOffset", + "type": "band", + "domain": {"data": "source_0", "field": "symbol", "sort": true}, + "range": [ + { + "signal": "-0.4 * (scale('x', datetime(2001, 1, 1, 0, 0, 0, 0)) - scale('x', datetime(2001, 0, 1, 0, 0, 0, 0)))" + }, + { + "signal": "0.4 * (scale('x', datetime(2001, 1, 1, 0, 0, 0, 0)) - scale('x', datetime(2001, 0, 1, 0, 0, 0, 0)))" + } + ] + }, + { + "name": "color", + "type": "ordinal", + "domain": {"data": "source_0", "field": "symbol", "sort": true}, + "range": "category" + } + ], + "axes": [ + { + "scale": "x", + "orient": "bottom", + "gridScale": "y", + "grid": true, + "tickCount": {"signal": "ceil(width/40)"}, + "tickMinStep": { + "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "domain": false, + "labels": false, + "aria": false, + "maxExtent": 0, + "minExtent": 0, + "ticks": false, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "gridScale": "x", + "grid": true, + "tickCount": {"signal": "ceil(height/40)"}, + "domain": false, + "labels": false, + "aria": false, + "maxExtent": 0, + "minExtent": 0, + "ticks": false, + "zindex": 0 + }, + { + "scale": "x", + "orient": "bottom", + "grid": false, + "title": "date", + "format": { + "signal": "timeUnitSpecifier([\"year\",\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"})" + }, + "labelFlush": true, + "labelOverlap": true, + "tickCount": {"signal": "ceil(width/40)"}, + "tickMinStep": { + "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "grid": false, + "title": "price", + "labelOverlap": true, + "tickCount": {"signal": "ceil(height/40)"}, + "zindex": 0 + } + ], + "legends": [{"fill": "color", "symbolType": "square", "title": "symbol"}] +} diff --git a/examples/compiled/bar_binned_yearmonth_label_band_center.png b/examples/compiled/bar_binned_yearmonth_label_band_center.png new file mode 100644 index 0000000000000000000000000000000000000000..76e09f342dc74a367b8c49c30e9b4873a49ac984 GIT binary patch literal 9995 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 Dl5x>6 literal 0 HcmV?d00001 diff --git a/examples/compiled/bar_binned_yearmonth_label_band_center.svg b/examples/compiled/bar_binned_yearmonth_label_band_center.svg new file mode 100644 index 00000000000..010afea05f9 --- /dev/null +++ b/examples/compiled/bar_binned_yearmonth_label_band_center.svg @@ -0,0 +1 @@ +Jan 2005Feb 2005Mar 2005date050100150200price195.62187.99180.51 \ No newline at end of file diff --git a/examples/compiled/bar_binned_yearmonth_label_band_center.vg.json b/examples/compiled/bar_binned_yearmonth_label_band_center.vg.json new file mode 100644 index 00000000000..91204d23343 --- /dev/null +++ b/examples/compiled/bar_binned_yearmonth_label_band_center.vg.json @@ -0,0 +1,205 @@ +{ + "$schema": "https://vega.github.io/schema/vega/v5.json", + "description": "Google's stock price over time.", + "background": "white", + "padding": 5, + "width": 200, + "height": 200, + "style": "cell", + "data": [ + { + "name": "source_0", + "url": "data/stocks.csv", + "format": {"type": "csv", "parse": {"date": "date"}}, + "transform": [ + {"type": "filter", "expr": "datum.symbol==='GOOG'"}, + { + "type": "filter", + "expr": "inrange(time(datum[\"date\"]), [time(datetime(2005, 0, 1, 0, 0, 0, 0)), time(datetime(2005, 2, 1, 0, 0, 0, 0))])" + } + ] + }, + { + "name": "data_0", + "source": "source_0", + "transform": [ + { + "type": "formula", + "expr": "timeOffset('month', datum['date'], 1)", + "as": "date_end" + }, + { + "type": "formula", + "expr": "0.5 * timeOffset('month', datum['date'], -1) + 0.5 * datum['date']", + "as": "date_offsetted_rect_start" + }, + { + "type": "formula", + "expr": "0.5 * datum['date'] + 0.5 * datum['date_end']", + "as": "date_offsetted_rect_end" + }, + { + "type": "stack", + "groupby": ["date"], + "field": "price", + "sort": {"field": [], "order": []}, + "as": ["price_start", "price_end"], + "offset": "zero" + }, + { + "type": "filter", + "expr": "(isDate(datum[\"date\"]) || (isValid(datum[\"date\"]) && isFinite(+datum[\"date\"]))) && isValid(datum[\"price\"]) && isFinite(+datum[\"price\"])" + } + ] + }, + { + "name": "data_1", + "source": "source_0", + "transform": [ + { + "type": "filter", + "expr": "(isDate(datum[\"date\"]) || (isValid(datum[\"date\"]) && isFinite(+datum[\"date\"]))) && isValid(datum[\"price\"]) && isFinite(+datum[\"price\"])" + } + ] + } + ], + "marks": [ + { + "name": "layer_0_marks", + "type": "rect", + "style": ["bar"], + "from": {"data": "data_0"}, + "encode": { + "update": { + "fill": {"value": "#4c78a8"}, + "ariaRoleDescription": {"value": "bar"}, + "description": { + "signal": "\"date: \" + (timeFormat(datum[\"date\"], timeUnitSpecifier([\"year\",\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"}))) + \"; price: \" + (format(datum[\"price\"], \"\"))" + }, + "x2": { + "scale": "x", + "field": "date_offsetted_rect_start", + "offset": { + "signal": "0.5 + (abs(scale(\"x\", datum[\"date_end\"]) - scale(\"x\", datum[\"date\"])) < 0.25 ? -0.5 * (0.25 - (abs(scale(\"x\", datum[\"date_end\"]) - scale(\"x\", datum[\"date\"])))) : 0.5)" + } + }, + "x": { + "scale": "x", + "field": "date_offsetted_rect_end", + "offset": { + "signal": "0.5 + (abs(scale(\"x\", datum[\"date_end\"]) - scale(\"x\", datum[\"date\"])) < 0.25 ? 0.5 * (0.25 - (abs(scale(\"x\", datum[\"date_end\"]) - scale(\"x\", datum[\"date\"])))) : -0.5)" + } + }, + "y": {"scale": "y", "field": "price_end"}, + "y2": {"scale": "y", "field": "price_start"} + } + } + }, + { + "name": "layer_1_marks", + "type": "text", + "style": ["text"], + "from": {"data": "data_1"}, + "encode": { + "update": { + "baseline": {"value": "bottom"}, + "fill": {"value": "black"}, + "description": { + "signal": "\"date: \" + (timeFormat(datum[\"date\"], timeUnitSpecifier([\"year\",\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"}))) + \"; price: \" + (format(datum[\"price\"], \"\"))" + }, + "x": {"scale": "x", "field": "date"}, + "y": {"scale": "y", "field": "price"}, + "text": {"signal": "format(datum[\"price\"], \"\")"}, + "align": {"value": "center"} + } + } + } + ], + "scales": [ + { + "name": "x", + "type": "time", + "domain": { + "fields": [ + {"data": "data_0", "field": "date_offsetted_rect_start"}, + {"data": "data_0", "field": "date_offsetted_rect_end"}, + {"data": "data_1", "field": "date"}, + {"data": "data_1", "field": "date_end"} + ] + }, + "range": [0, {"signal": "width"}] + }, + { + "name": "y", + "type": "linear", + "domain": { + "fields": [ + {"data": "data_0", "field": "price_start"}, + {"data": "data_0", "field": "price_end"}, + {"data": "data_1", "field": "price"} + ] + }, + "range": [{"signal": "height"}, 0], + "nice": true, + "zero": true + } + ], + "axes": [ + { + "scale": "x", + "orient": "bottom", + "gridScale": "y", + "grid": true, + "tickCount": {"signal": "ceil(width/40)"}, + "tickMinStep": { + "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "domain": false, + "labels": false, + "aria": false, + "maxExtent": 0, + "minExtent": 0, + "ticks": false, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "gridScale": "x", + "grid": true, + "tickCount": {"signal": "ceil(height/40)"}, + "domain": false, + "labels": false, + "aria": false, + "maxExtent": 0, + "minExtent": 0, + "ticks": false, + "zindex": 0 + }, + { + "scale": "x", + "orient": "bottom", + "grid": false, + "title": "date", + "format": { + "signal": "timeUnitSpecifier([\"year\",\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"})" + }, + "labelFlush": true, + "labelOverlap": true, + "tickCount": {"signal": "ceil(width/40)"}, + "tickMinStep": { + "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "grid": false, + "title": "price", + "labelOverlap": true, + "tickCount": {"signal": "ceil(height/40)"}, + "zindex": 0 + } + ] +} diff --git a/examples/compiled/bar_month_temporal_band_center.png b/examples/compiled/bar_month_temporal_band_center.png new file mode 100644 index 0000000000000000000000000000000000000000..62203cbc3b991e1de3b78ea5fcc390337c8e59eb GIT binary patch literal 9999 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

(pNEdYsd+6*{{ z=kn#t&tJamn#5ukEIdKflOd1D0+A5P z+pKx}HXeYzA)*NpLmZ&*m(o%bh~`6hn}Ig#5FR=@x@j!70i+e~6GB45#nf;~LwNOq z46%Fl>N!;)=1H~NyUsXXA3OuF!Md)jkpZpGZr*5y1cJ!xPP(f5ky!{4yVa$+A1V0w zwf7T6n?S~Efroz^Ra#aS$0TBlu^cY&zOGe-!7~7zXAr{s3KC=@uf5$}>n$b;m$!HZ zW91G;fByVg1nxFL;z~;$6PF&u@p$7I4CU+3gblE*0DWiyD4q+HB}0ByyNk7Vbv;Mo zpekTRVJb!B^~>QIXa+Lm0>3HBXc6WfFm8g+V9Y_}#U$ybnRy7&hVFqttJu?ef=?=( z!)bTk&?8?R7SxyD$0y-}wBiH8L-HTSc9$YspFs)Zut!;08HY)bf8#p|wv_AFcqrbv zgBx)hSXqJS>*`Y1((g1jh52Ra?b3BS1#A2OxFeJUVHr`xGxSD-b&^kgxIixno4UbA5MBb8jzI?H49FJqLb+ z0Yz~Mi8^}hs{kjD&jBFw)6&wCVQX>OL8*>>XgtR4n4$~{4Ed1jF#g4a!Vn^V2v2Ow zUvxe@8XX`1|DQp`_r}SxsfBwFWz*o zyj;)vIAzHOG7xpfmxP3*b|JqekH2kkY6>H0IRfqn|B`%2Mv1~(F!H)joev|HrVkx03?Q`GUOQP>w@D zTV2zyem}HO;jlo2(2Kpg2DsU2nfoXS6f#bwSGjEUaxT}LE4O&bhEwc-(-6P(Kwu5P zhkG$#b|9Nh`QAMnAhE??r4*pn=_TEStB?21;R`^GY2fweju0UHAqa7t zEwldHM)Uozt&@w3Z9;OD>2SeQ2p$50gB$!=JV+*fE>i%f|8@C`dxZsXXf%w5l=j+0 z%vC{PGe~5wDkLa`F`foz`BEbo51*(O#M1>4J#e0&z}Fz9Ni8a(g4-b>Bg0fxNesC{ zmTK7ZQ4R-gUM3osA>qVspK%@NsmEvx0}bP585enZBM&UhzE5|&^MO$m76dyC#!MHj z8JO5txRg^=VDw7@Zd^D2<#%v7XcWm?=F#J;sknE=dc0` \ No newline at end of file diff --git a/examples/compiled/bar_month_temporal_band_center.vg.json b/examples/compiled/bar_month_temporal_band_center.vg.json new file mode 100644 index 00000000000..597691718b3 --- /dev/null +++ b/examples/compiled/bar_month_temporal_band_center.vg.json @@ -0,0 +1,160 @@ +{ + "$schema": "https://vega.github.io/schema/vega/v5.json", + "background": "white", + "padding": 5, + "width": 200, + "height": 200, + "style": "cell", + "data": [ + { + "name": "source_0", + "url": "data/seattle-weather.csv", + "format": {"type": "csv", "parse": {"date": "date"}}, + "transform": [ + { + "field": "date", + "type": "timeunit", + "units": ["month"], + "as": ["month_date", "month_date_end"] + }, + { + "type": "formula", + "expr": "0.5 * timeOffset('month', datum['month_date'], -1) + 0.5 * datum['month_date']", + "as": "month_date_offsetted_rect_start" + }, + { + "type": "formula", + "expr": "0.5 * datum['month_date'] + 0.5 * datum['month_date_end']", + "as": "month_date_offsetted_rect_end" + }, + { + "type": "aggregate", + "groupby": [ + "month_date", + "month_date_end", + "month_date_offsetted_rect_start", + "month_date_offsetted_rect_end" + ], + "ops": ["mean"], + "fields": ["precipitation"], + "as": ["mean_precipitation"] + }, + { + "type": "filter", + "expr": "(isDate(datum[\"month_date\"]) || (isValid(datum[\"month_date\"]) && isFinite(+datum[\"month_date\"]))) && isValid(datum[\"mean_precipitation\"]) && isFinite(+datum[\"mean_precipitation\"])" + } + ] + } + ], + "marks": [ + { + "name": "marks", + "type": "rect", + "style": ["bar"], + "from": {"data": "source_0"}, + "encode": { + "update": { + "fill": {"value": "#4c78a8"}, + "ariaRoleDescription": {"value": "bar"}, + "description": { + "signal": "\"date (month): \" + (timeFormat(datum[\"month_date\"], timeUnitSpecifier([\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"}))) + \"; Mean of precipitation: \" + (format(datum[\"mean_precipitation\"], \"\"))" + }, + "x2": { + "scale": "x", + "field": "month_date_offsetted_rect_start", + "offset": { + "signal": "0.5 + (abs(scale(\"x\", datum[\"month_date_end\"]) - scale(\"x\", datum[\"month_date\"])) < 0.25 ? -0.5 * (0.25 - (abs(scale(\"x\", datum[\"month_date_end\"]) - scale(\"x\", datum[\"month_date\"])))) : 0.5)" + } + }, + "x": { + "scale": "x", + "field": "month_date_offsetted_rect_end", + "offset": { + "signal": "0.5 + (abs(scale(\"x\", datum[\"month_date_end\"]) - scale(\"x\", datum[\"month_date\"])) < 0.25 ? 0.5 * (0.25 - (abs(scale(\"x\", datum[\"month_date_end\"]) - scale(\"x\", datum[\"month_date\"])))) : -0.5)" + } + }, + "y": {"scale": "y", "field": "mean_precipitation"}, + "y2": {"scale": "y", "value": 0} + } + } + } + ], + "scales": [ + { + "name": "x", + "type": "time", + "domain": { + "data": "source_0", + "fields": [ + "month_date_offsetted_rect_start", + "month_date_offsetted_rect_end" + ] + }, + "range": [0, {"signal": "width"}] + }, + { + "name": "y", + "type": "linear", + "domain": {"data": "source_0", "field": "mean_precipitation"}, + "range": [{"signal": "height"}, 0], + "nice": true, + "zero": true + } + ], + "axes": [ + { + "scale": "x", + "orient": "bottom", + "gridScale": "y", + "grid": true, + "tickMinStep": { + "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "domain": false, + "labels": false, + "aria": false, + "maxExtent": 0, + "minExtent": 0, + "ticks": false, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "gridScale": "x", + "grid": true, + "tickCount": {"signal": "ceil(height/40)"}, + "domain": false, + "labels": false, + "aria": false, + "maxExtent": 0, + "minExtent": 0, + "ticks": false, + "zindex": 0 + }, + { + "scale": "x", + "orient": "bottom", + "grid": false, + "title": "date (month)", + "format": { + "signal": "timeUnitSpecifier([\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"})" + }, + "labelFlush": true, + "labelOverlap": true, + "tickMinStep": { + "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "grid": false, + "title": "Mean of precipitation", + "labelOverlap": true, + "tickCount": {"signal": "ceil(height/40)"}, + "zindex": 0 + } + ] +} diff --git a/examples/compiled/bar_month_temporal_band_center_config.png b/examples/compiled/bar_month_temporal_band_center_config.png new file mode 100644 index 0000000000000000000000000000000000000000..62203cbc3b991e1de3b78ea5fcc390337c8e59eb GIT binary patch literal 9999 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

(pNEdYsd+6*{{ z=kn#t&tJamn#5ukEIdKflOd1D0+A5P z+pKx}HXeYzA)*NpLmZ&*m(o%bh~`6hn}Ig#5FR=@x@j!70i+e~6GB45#nf;~LwNOq z46%Fl>N!;)=1H~NyUsXXA3OuF!Md)jkpZpGZr*5y1cJ!xPP(f5ky!{4yVa$+A1V0w zwf7T6n?S~Efroz^Ra#aS$0TBlu^cY&zOGe-!7~7zXAr{s3KC=@uf5$}>n$b;m$!HZ zW91G;fByVg1nxFL;z~;$6PF&u@p$7I4CU+3gblE*0DWiyD4q+HB}0ByyNk7Vbv;Mo zpekTRVJb!B^~>QIXa+Lm0>3HBXc6WfFm8g+V9Y_}#U$ybnRy7&hVFqttJu?ef=?=( z!)bTk&?8?R7SxyD$0y-}wBiH8L-HTSc9$YspFs)Zut!;08HY)bf8#p|wv_AFcqrbv zgBx)hSXqJS>*`Y1((g1jh52Ra?b3BS1#A2OxFeJUVHr`xGxSD-b&^kgxIixno4UbA5MBb8jzI?H49FJqLb+ z0Yz~Mi8^}hs{kjD&jBFw)6&wCVQX>OL8*>>XgtR4n4$~{4Ed1jF#g4a!Vn^V2v2Ow zUvxe@8XX`1|DQp`_r}SxsfBwFWz*o zyj;)vIAzHOG7xpfmxP3*b|JqekH2kkY6>H0IRfqn|B`%2Mv1~(F!H)joev|HrVkx03?Q`GUOQP>w@D zTV2zyem}HO;jlo2(2Kpg2DsU2nfoXS6f#bwSGjEUaxT}LE4O&bhEwc-(-6P(Kwu5P zhkG$#b|9Nh`QAMnAhE??r4*pn=_TEStB?21;R`^GY2fweju0UHAqa7t zEwldHM)Uozt&@w3Z9;OD>2SeQ2p$50gB$!=JV+*fE>i%f|8@C`dxZsXXf%w5l=j+0 z%vC{PGe~5wDkLa`F`foz`BEbo51*(O#M1>4J#e0&z}Fz9Ni8a(g4-b>Bg0fxNesC{ zmTK7ZQ4R-gUM3osA>qVspK%@NsmEvx0}bP585enZBM&UhzE5|&^MO$m76dyC#!MHj z8JO5txRg^=VDw7@Zd^D2<#%v7XcWm?=F#J;sknE=dc0` \ No newline at end of file diff --git a/examples/compiled/bar_month_temporal_band_center_config.vg.json b/examples/compiled/bar_month_temporal_band_center_config.vg.json new file mode 100644 index 00000000000..597691718b3 --- /dev/null +++ b/examples/compiled/bar_month_temporal_band_center_config.vg.json @@ -0,0 +1,160 @@ +{ + "$schema": "https://vega.github.io/schema/vega/v5.json", + "background": "white", + "padding": 5, + "width": 200, + "height": 200, + "style": "cell", + "data": [ + { + "name": "source_0", + "url": "data/seattle-weather.csv", + "format": {"type": "csv", "parse": {"date": "date"}}, + "transform": [ + { + "field": "date", + "type": "timeunit", + "units": ["month"], + "as": ["month_date", "month_date_end"] + }, + { + "type": "formula", + "expr": "0.5 * timeOffset('month', datum['month_date'], -1) + 0.5 * datum['month_date']", + "as": "month_date_offsetted_rect_start" + }, + { + "type": "formula", + "expr": "0.5 * datum['month_date'] + 0.5 * datum['month_date_end']", + "as": "month_date_offsetted_rect_end" + }, + { + "type": "aggregate", + "groupby": [ + "month_date", + "month_date_end", + "month_date_offsetted_rect_start", + "month_date_offsetted_rect_end" + ], + "ops": ["mean"], + "fields": ["precipitation"], + "as": ["mean_precipitation"] + }, + { + "type": "filter", + "expr": "(isDate(datum[\"month_date\"]) || (isValid(datum[\"month_date\"]) && isFinite(+datum[\"month_date\"]))) && isValid(datum[\"mean_precipitation\"]) && isFinite(+datum[\"mean_precipitation\"])" + } + ] + } + ], + "marks": [ + { + "name": "marks", + "type": "rect", + "style": ["bar"], + "from": {"data": "source_0"}, + "encode": { + "update": { + "fill": {"value": "#4c78a8"}, + "ariaRoleDescription": {"value": "bar"}, + "description": { + "signal": "\"date (month): \" + (timeFormat(datum[\"month_date\"], timeUnitSpecifier([\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"}))) + \"; Mean of precipitation: \" + (format(datum[\"mean_precipitation\"], \"\"))" + }, + "x2": { + "scale": "x", + "field": "month_date_offsetted_rect_start", + "offset": { + "signal": "0.5 + (abs(scale(\"x\", datum[\"month_date_end\"]) - scale(\"x\", datum[\"month_date\"])) < 0.25 ? -0.5 * (0.25 - (abs(scale(\"x\", datum[\"month_date_end\"]) - scale(\"x\", datum[\"month_date\"])))) : 0.5)" + } + }, + "x": { + "scale": "x", + "field": "month_date_offsetted_rect_end", + "offset": { + "signal": "0.5 + (abs(scale(\"x\", datum[\"month_date_end\"]) - scale(\"x\", datum[\"month_date\"])) < 0.25 ? 0.5 * (0.25 - (abs(scale(\"x\", datum[\"month_date_end\"]) - scale(\"x\", datum[\"month_date\"])))) : -0.5)" + } + }, + "y": {"scale": "y", "field": "mean_precipitation"}, + "y2": {"scale": "y", "value": 0} + } + } + } + ], + "scales": [ + { + "name": "x", + "type": "time", + "domain": { + "data": "source_0", + "fields": [ + "month_date_offsetted_rect_start", + "month_date_offsetted_rect_end" + ] + }, + "range": [0, {"signal": "width"}] + }, + { + "name": "y", + "type": "linear", + "domain": {"data": "source_0", "field": "mean_precipitation"}, + "range": [{"signal": "height"}, 0], + "nice": true, + "zero": true + } + ], + "axes": [ + { + "scale": "x", + "orient": "bottom", + "gridScale": "y", + "grid": true, + "tickMinStep": { + "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "domain": false, + "labels": false, + "aria": false, + "maxExtent": 0, + "minExtent": 0, + "ticks": false, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "gridScale": "x", + "grid": true, + "tickCount": {"signal": "ceil(height/40)"}, + "domain": false, + "labels": false, + "aria": false, + "maxExtent": 0, + "minExtent": 0, + "ticks": false, + "zindex": 0 + }, + { + "scale": "x", + "orient": "bottom", + "grid": false, + "title": "date (month)", + "format": { + "signal": "timeUnitSpecifier([\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"})" + }, + "labelFlush": true, + "labelOverlap": true, + "tickMinStep": { + "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "grid": false, + "title": "Mean of precipitation", + "labelOverlap": true, + "tickCount": {"signal": "ceil(height/40)"}, + "zindex": 0 + } + ] +} diff --git a/examples/compiled/bar_simple_binned_timeunit_special_chars.vg.json b/examples/compiled/bar_simple_binned_timeunit_special_chars.vg.json index 191f43dd0da..2fb873170ff 100644 --- a/examples/compiled/bar_simple_binned_timeunit_special_chars.vg.json +++ b/examples/compiled/bar_simple_binned_timeunit_special_chars.vg.json @@ -31,7 +31,7 @@ {"type": "formula", "expr": "toDate(datum[\"a.b\"])", "as": "a.b"}, { "type": "formula", - "expr": "timeOffset('date', datum['a.b'], 1)", + "expr": "utcOffset('date', datum['a.b'], 1)", "as": "a.b_end" }, { diff --git a/examples/compiled/bar_yearmonth_center_band.png b/examples/compiled/bar_yearmonth_center_band.png new file mode 100644 index 0000000000000000000000000000000000000000..4ac977a0c831eeebcdf1112f5588ccf1b148647b GIT binary patch literal 14127 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 literal 0 HcmV?d00001 diff --git a/examples/compiled/bar_yearmonth_center_band.svg b/examples/compiled/bar_yearmonth_center_band.svg new file mode 100644 index 00000000000..4e3fa4a8cc7 --- /dev/null +++ b/examples/compiled/bar_yearmonth_center_band.svg @@ -0,0 +1 @@ +Jan 2012Jan 2013Jan 2014Jan 2015date (year-month)051015202530Mean of temp_max \ No newline at end of file diff --git a/examples/compiled/bar_yearmonth_center_band.vg.json b/examples/compiled/bar_yearmonth_center_band.vg.json new file mode 100644 index 00000000000..4886b6c607b --- /dev/null +++ b/examples/compiled/bar_yearmonth_center_band.vg.json @@ -0,0 +1,163 @@ +{ + "$schema": "https://vega.github.io/schema/vega/v5.json", + "description": "Temperature in Seattle as a bar chart with yearmonth time unit.", + "background": "white", + "padding": 5, + "width": 200, + "height": 200, + "style": "cell", + "data": [ + { + "name": "source_0", + "url": "data/seattle-weather.csv", + "format": {"type": "csv", "parse": {"date": "date"}}, + "transform": [ + { + "field": "date", + "type": "timeunit", + "units": ["year", "month"], + "as": ["yearmonth_date", "yearmonth_date_end"] + }, + { + "type": "formula", + "expr": "0.5 * timeOffset('month', datum['yearmonth_date'], -1) + 0.5 * datum['yearmonth_date']", + "as": "yearmonth_date_offsetted_rect_start" + }, + { + "type": "formula", + "expr": "0.5 * datum['yearmonth_date'] + 0.5 * datum['yearmonth_date_end']", + "as": "yearmonth_date_offsetted_rect_end" + }, + { + "type": "aggregate", + "groupby": [ + "yearmonth_date", + "yearmonth_date_end", + "yearmonth_date_offsetted_rect_start", + "yearmonth_date_offsetted_rect_end" + ], + "ops": ["mean"], + "fields": ["temp_max"], + "as": ["mean_temp_max"] + }, + { + "type": "filter", + "expr": "(isDate(datum[\"yearmonth_date\"]) || (isValid(datum[\"yearmonth_date\"]) && isFinite(+datum[\"yearmonth_date\"]))) && isValid(datum[\"mean_temp_max\"]) && isFinite(+datum[\"mean_temp_max\"])" + } + ] + } + ], + "marks": [ + { + "name": "marks", + "type": "rect", + "style": ["bar"], + "from": {"data": "source_0"}, + "encode": { + "update": { + "fill": {"value": "#4c78a8"}, + "ariaRoleDescription": {"value": "bar"}, + "description": { + "signal": "\"date (year-month): \" + (timeFormat(datum[\"yearmonth_date\"], timeUnitSpecifier([\"year\",\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"}))) + \"; Mean of temp_max: \" + (format(datum[\"mean_temp_max\"], \"\"))" + }, + "x2": { + "scale": "x", + "field": "yearmonth_date_offsetted_rect_start", + "offset": { + "signal": "0.5 + (abs(scale(\"x\", datum[\"yearmonth_date_end\"]) - scale(\"x\", datum[\"yearmonth_date\"])) < 0.25 ? -0.5 * (0.25 - (abs(scale(\"x\", datum[\"yearmonth_date_end\"]) - scale(\"x\", datum[\"yearmonth_date\"])))) : 0.5)" + } + }, + "x": { + "scale": "x", + "field": "yearmonth_date_offsetted_rect_end", + "offset": { + "signal": "0.5 + (abs(scale(\"x\", datum[\"yearmonth_date_end\"]) - scale(\"x\", datum[\"yearmonth_date\"])) < 0.25 ? 0.5 * (0.25 - (abs(scale(\"x\", datum[\"yearmonth_date_end\"]) - scale(\"x\", datum[\"yearmonth_date\"])))) : -0.5)" + } + }, + "y": {"scale": "y", "field": "mean_temp_max"}, + "y2": {"scale": "y", "value": 0} + } + } + } + ], + "scales": [ + { + "name": "x", + "type": "time", + "domain": { + "data": "source_0", + "fields": [ + "yearmonth_date_offsetted_rect_start", + "yearmonth_date_offsetted_rect_end" + ] + }, + "range": [0, {"signal": "width"}] + }, + { + "name": "y", + "type": "linear", + "domain": {"data": "source_0", "field": "mean_temp_max"}, + "range": [{"signal": "height"}, 0], + "nice": true, + "zero": true + } + ], + "axes": [ + { + "scale": "x", + "orient": "bottom", + "gridScale": "y", + "grid": true, + "tickCount": {"signal": "ceil(width/40)"}, + "tickMinStep": { + "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "domain": false, + "labels": false, + "aria": false, + "maxExtent": 0, + "minExtent": 0, + "ticks": false, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "gridScale": "x", + "grid": true, + "tickCount": {"signal": "ceil(height/40)"}, + "domain": false, + "labels": false, + "aria": false, + "maxExtent": 0, + "minExtent": 0, + "ticks": false, + "zindex": 0 + }, + { + "scale": "x", + "orient": "bottom", + "grid": false, + "title": "date (year-month)", + "format": { + "signal": "timeUnitSpecifier([\"year\",\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"})" + }, + "labelFlush": true, + "labelOverlap": true, + "tickCount": {"signal": "ceil(width/40)"}, + "tickMinStep": { + "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "grid": false, + "title": "Mean of temp_max", + "labelOverlap": true, + "tickCount": {"signal": "ceil(height/40)"}, + "zindex": 0 + } + ] +} diff --git a/examples/compiled/rect_heatmap_weather_temporal_center_band.png b/examples/compiled/rect_heatmap_weather_temporal_center_band.png new file mode 100644 index 0000000000000000000000000000000000000000..1e05b81d864d220e7bca46a2fe288794cf228931 GIT binary patch literal 24755 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 X%2#^NPCw;__aj5;o9Y$o+<5qZW}hQ7 literal 0 HcmV?d00001 diff --git a/examples/compiled/rect_heatmap_weather_temporal_center_band.svg b/examples/compiled/rect_heatmap_weather_temporal_center_band.svg new file mode 100644 index 00000000000..52c09d0b29b --- /dev/null +++ b/examples/compiled/rect_heatmap_weather_temporal_center_band.svg @@ -0,0 +1 @@ +18152229DayJanFebMarAprMayJunJulAugSepOctNovDecMonth102030Daily Max Temperatures (C) in Seattle, WA \ No newline at end of file diff --git a/examples/compiled/rect_heatmap_weather_temporal_center_band.vg.json b/examples/compiled/rect_heatmap_weather_temporal_center_band.vg.json new file mode 100644 index 00000000000..ddafffe73d9 --- /dev/null +++ b/examples/compiled/rect_heatmap_weather_temporal_center_band.vg.json @@ -0,0 +1,223 @@ +{ + "$schema": "https://vega.github.io/schema/vega/v5.json", + "background": "white", + "padding": 5, + "width": 200, + "height": 200, + "title": { + "text": "Daily Max Temperatures (C) in Seattle, WA", + "frame": "group" + }, + "style": "cell", + "data": [ + { + "name": "source_0", + "url": "data/seattle-weather.csv", + "format": { + "type": "csv", + "parse": {"date": "date", "temp_max": "number"} + }, + "transform": [ + { + "field": "date", + "type": "timeunit", + "units": ["date"], + "as": ["date_date", "date_date_end"] + }, + { + "type": "formula", + "expr": "0.5 * timeOffset('date', datum['date_date'], -1) + 0.5 * datum['date_date']", + "as": "date_date_offsetted_rect_start" + }, + { + "type": "formula", + "expr": "0.5 * datum['date_date'] + 0.5 * datum['date_date_end']", + "as": "date_date_offsetted_rect_end" + }, + { + "field": "date", + "type": "timeunit", + "units": ["month"], + "as": ["month_date", "month_date_end"] + }, + { + "type": "formula", + "expr": "0.5 * timeOffset('month', datum['month_date'], -1) + 0.5 * datum['month_date']", + "as": "month_date_offsetted_rect_start" + }, + { + "type": "formula", + "expr": "0.5 * datum['month_date'] + 0.5 * datum['month_date_end']", + "as": "month_date_offsetted_rect_end" + }, + { + "type": "aggregate", + "groupby": [ + "date_date", + "date_date_end", + "date_date_offsetted_rect_start", + "date_date_offsetted_rect_end", + "month_date", + "month_date_end", + "month_date_offsetted_rect_start", + "month_date_offsetted_rect_end" + ], + "ops": ["max"], + "fields": ["temp_max"], + "as": ["max_temp_max"] + }, + { + "type": "filter", + "expr": "(isDate(datum[\"month_date\"]) || (isValid(datum[\"month_date\"]) && isFinite(+datum[\"month_date\"]))) && isValid(datum[\"max_temp_max\"]) && isFinite(+datum[\"max_temp_max\"])" + } + ] + } + ], + "marks": [ + { + "name": "marks", + "type": "rect", + "style": ["rect"], + "from": {"data": "source_0"}, + "encode": { + "update": { + "fill": {"scale": "color", "field": "max_temp_max"}, + "description": { + "signal": "\"Day: \" + (timeFormat(datum[\"date_date\"], '%e')) + \"; Month: \" + (timeFormat(datum[\"month_date\"], timeUnitSpecifier([\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"}))) + \"; Max of temp_max: \" + (format(datum[\"max_temp_max\"], \"\"))" + }, + "x2": { + "scale": "x", + "field": "date_date_offsetted_rect_start", + "offset": { + "signal": "0.5 + (abs(scale(\"x\", datum[\"date_date_end\"]) - scale(\"x\", datum[\"date_date\"])) < 0.25 ? -0.5 * (0.25 - (abs(scale(\"x\", datum[\"date_date_end\"]) - scale(\"x\", datum[\"date_date\"])))) : 0)" + } + }, + "x": { + "scale": "x", + "field": "date_date_offsetted_rect_end", + "offset": { + "signal": "0.5 + (abs(scale(\"x\", datum[\"date_date_end\"]) - scale(\"x\", datum[\"date_date\"])) < 0.25 ? 0.5 * (0.25 - (abs(scale(\"x\", datum[\"date_date_end\"]) - scale(\"x\", datum[\"date_date\"])))) : 0)" + } + }, + "y2": { + "scale": "y", + "field": "month_date_offsetted_rect_start", + "offset": { + "signal": "0.5 + (abs(scale(\"y\", datum[\"month_date_end\"]) - scale(\"y\", datum[\"month_date\"])) < 0.25 ? 0.5 * (0.25 - (abs(scale(\"y\", datum[\"month_date_end\"]) - scale(\"y\", datum[\"month_date\"])))) : 0)" + } + }, + "y": { + "scale": "y", + "field": "month_date_offsetted_rect_end", + "offset": { + "signal": "0.5 + (abs(scale(\"y\", datum[\"month_date_end\"]) - scale(\"y\", datum[\"month_date\"])) < 0.25 ? -0.5 * (0.25 - (abs(scale(\"y\", datum[\"month_date_end\"]) - scale(\"y\", datum[\"month_date\"])))) : 0)" + } + } + } + } + } + ], + "scales": [ + { + "name": "x", + "type": "time", + "domain": { + "data": "source_0", + "fields": [ + "date_date_offsetted_rect_start", + "date_date_offsetted_rect_end" + ] + }, + "range": [0, {"signal": "width"}] + }, + { + "name": "y", + "type": "time", + "domain": { + "data": "source_0", + "fields": [ + "month_date_offsetted_rect_start", + "month_date_offsetted_rect_end" + ] + }, + "range": [{"signal": "height"}, 0] + }, + { + "name": "color", + "type": "linear", + "domain": {"data": "source_0", "field": "max_temp_max"}, + "range": "heatmap", + "interpolate": "hcl", + "zero": false + } + ], + "axes": [ + { + "scale": "x", + "orient": "bottom", + "gridScale": "y", + "grid": true, + "tickCount": {"signal": "ceil(width/40)"}, + "tickMinStep": { + "signal": "datetime(2001, 0, 2, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "domain": false, + "labels": false, + "aria": false, + "maxExtent": 0, + "minExtent": 0, + "ticks": false, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "gridScale": "x", + "grid": true, + "tickMinStep": { + "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "domain": false, + "labels": false, + "aria": false, + "maxExtent": 0, + "minExtent": 0, + "ticks": false, + "zindex": 0 + }, + { + "scale": "x", + "orient": "bottom", + "grid": false, + "title": "Day", + "format": "%e", + "labelAngle": 0, + "labelBaseline": "top", + "labelFlush": true, + "labelOverlap": true, + "tickCount": {"signal": "ceil(width/40)"}, + "tickMinStep": { + "signal": "datetime(2001, 0, 2, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "grid": false, + "title": "Month", + "format": { + "signal": "timeUnitSpecifier([\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"})" + }, + "labelOverlap": true, + "tickMinStep": { + "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "zindex": 0 + } + ], + "legends": [ + {"fill": "color", "gradientLength": {"signal": "clamp(height, 64, 200)"}} + ], + "config": {"axis": {"domain": false}, "style": {"cell": {"strokeWidth": 0}}} +} diff --git a/examples/compiled/rect_heatmap_weather_temporal_center_band_config.png b/examples/compiled/rect_heatmap_weather_temporal_center_band_config.png new file mode 100644 index 0000000000000000000000000000000000000000..1e05b81d864d220e7bca46a2fe288794cf228931 GIT binary patch literal 24755 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 X%2#^NPCw;__aj5;o9Y$o+<5qZW}hQ7 literal 0 HcmV?d00001 diff --git a/examples/compiled/rect_heatmap_weather_temporal_center_band_config.svg b/examples/compiled/rect_heatmap_weather_temporal_center_band_config.svg new file mode 100644 index 00000000000..52c09d0b29b --- /dev/null +++ b/examples/compiled/rect_heatmap_weather_temporal_center_band_config.svg @@ -0,0 +1 @@ +18152229DayJanFebMarAprMayJunJulAugSepOctNovDecMonth102030Daily Max Temperatures (C) in Seattle, WA \ No newline at end of file diff --git a/examples/compiled/rect_heatmap_weather_temporal_center_band_config.vg.json b/examples/compiled/rect_heatmap_weather_temporal_center_band_config.vg.json new file mode 100644 index 00000000000..ddafffe73d9 --- /dev/null +++ b/examples/compiled/rect_heatmap_weather_temporal_center_band_config.vg.json @@ -0,0 +1,223 @@ +{ + "$schema": "https://vega.github.io/schema/vega/v5.json", + "background": "white", + "padding": 5, + "width": 200, + "height": 200, + "title": { + "text": "Daily Max Temperatures (C) in Seattle, WA", + "frame": "group" + }, + "style": "cell", + "data": [ + { + "name": "source_0", + "url": "data/seattle-weather.csv", + "format": { + "type": "csv", + "parse": {"date": "date", "temp_max": "number"} + }, + "transform": [ + { + "field": "date", + "type": "timeunit", + "units": ["date"], + "as": ["date_date", "date_date_end"] + }, + { + "type": "formula", + "expr": "0.5 * timeOffset('date', datum['date_date'], -1) + 0.5 * datum['date_date']", + "as": "date_date_offsetted_rect_start" + }, + { + "type": "formula", + "expr": "0.5 * datum['date_date'] + 0.5 * datum['date_date_end']", + "as": "date_date_offsetted_rect_end" + }, + { + "field": "date", + "type": "timeunit", + "units": ["month"], + "as": ["month_date", "month_date_end"] + }, + { + "type": "formula", + "expr": "0.5 * timeOffset('month', datum['month_date'], -1) + 0.5 * datum['month_date']", + "as": "month_date_offsetted_rect_start" + }, + { + "type": "formula", + "expr": "0.5 * datum['month_date'] + 0.5 * datum['month_date_end']", + "as": "month_date_offsetted_rect_end" + }, + { + "type": "aggregate", + "groupby": [ + "date_date", + "date_date_end", + "date_date_offsetted_rect_start", + "date_date_offsetted_rect_end", + "month_date", + "month_date_end", + "month_date_offsetted_rect_start", + "month_date_offsetted_rect_end" + ], + "ops": ["max"], + "fields": ["temp_max"], + "as": ["max_temp_max"] + }, + { + "type": "filter", + "expr": "(isDate(datum[\"month_date\"]) || (isValid(datum[\"month_date\"]) && isFinite(+datum[\"month_date\"]))) && isValid(datum[\"max_temp_max\"]) && isFinite(+datum[\"max_temp_max\"])" + } + ] + } + ], + "marks": [ + { + "name": "marks", + "type": "rect", + "style": ["rect"], + "from": {"data": "source_0"}, + "encode": { + "update": { + "fill": {"scale": "color", "field": "max_temp_max"}, + "description": { + "signal": "\"Day: \" + (timeFormat(datum[\"date_date\"], '%e')) + \"; Month: \" + (timeFormat(datum[\"month_date\"], timeUnitSpecifier([\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"}))) + \"; Max of temp_max: \" + (format(datum[\"max_temp_max\"], \"\"))" + }, + "x2": { + "scale": "x", + "field": "date_date_offsetted_rect_start", + "offset": { + "signal": "0.5 + (abs(scale(\"x\", datum[\"date_date_end\"]) - scale(\"x\", datum[\"date_date\"])) < 0.25 ? -0.5 * (0.25 - (abs(scale(\"x\", datum[\"date_date_end\"]) - scale(\"x\", datum[\"date_date\"])))) : 0)" + } + }, + "x": { + "scale": "x", + "field": "date_date_offsetted_rect_end", + "offset": { + "signal": "0.5 + (abs(scale(\"x\", datum[\"date_date_end\"]) - scale(\"x\", datum[\"date_date\"])) < 0.25 ? 0.5 * (0.25 - (abs(scale(\"x\", datum[\"date_date_end\"]) - scale(\"x\", datum[\"date_date\"])))) : 0)" + } + }, + "y2": { + "scale": "y", + "field": "month_date_offsetted_rect_start", + "offset": { + "signal": "0.5 + (abs(scale(\"y\", datum[\"month_date_end\"]) - scale(\"y\", datum[\"month_date\"])) < 0.25 ? 0.5 * (0.25 - (abs(scale(\"y\", datum[\"month_date_end\"]) - scale(\"y\", datum[\"month_date\"])))) : 0)" + } + }, + "y": { + "scale": "y", + "field": "month_date_offsetted_rect_end", + "offset": { + "signal": "0.5 + (abs(scale(\"y\", datum[\"month_date_end\"]) - scale(\"y\", datum[\"month_date\"])) < 0.25 ? -0.5 * (0.25 - (abs(scale(\"y\", datum[\"month_date_end\"]) - scale(\"y\", datum[\"month_date\"])))) : 0)" + } + } + } + } + } + ], + "scales": [ + { + "name": "x", + "type": "time", + "domain": { + "data": "source_0", + "fields": [ + "date_date_offsetted_rect_start", + "date_date_offsetted_rect_end" + ] + }, + "range": [0, {"signal": "width"}] + }, + { + "name": "y", + "type": "time", + "domain": { + "data": "source_0", + "fields": [ + "month_date_offsetted_rect_start", + "month_date_offsetted_rect_end" + ] + }, + "range": [{"signal": "height"}, 0] + }, + { + "name": "color", + "type": "linear", + "domain": {"data": "source_0", "field": "max_temp_max"}, + "range": "heatmap", + "interpolate": "hcl", + "zero": false + } + ], + "axes": [ + { + "scale": "x", + "orient": "bottom", + "gridScale": "y", + "grid": true, + "tickCount": {"signal": "ceil(width/40)"}, + "tickMinStep": { + "signal": "datetime(2001, 0, 2, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "domain": false, + "labels": false, + "aria": false, + "maxExtent": 0, + "minExtent": 0, + "ticks": false, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "gridScale": "x", + "grid": true, + "tickMinStep": { + "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "domain": false, + "labels": false, + "aria": false, + "maxExtent": 0, + "minExtent": 0, + "ticks": false, + "zindex": 0 + }, + { + "scale": "x", + "orient": "bottom", + "grid": false, + "title": "Day", + "format": "%e", + "labelAngle": 0, + "labelBaseline": "top", + "labelFlush": true, + "labelOverlap": true, + "tickCount": {"signal": "ceil(width/40)"}, + "tickMinStep": { + "signal": "datetime(2001, 0, 2, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "grid": false, + "title": "Month", + "format": { + "signal": "timeUnitSpecifier([\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"})" + }, + "labelOverlap": true, + "tickMinStep": { + "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)" + }, + "zindex": 0 + } + ], + "legends": [ + {"fill": "color", "gradientLength": {"signal": "clamp(height, 64, 200)"}} + ], + "config": {"axis": {"domain": false}, "style": {"cell": {"strokeWidth": 0}}} +} diff --git a/examples/compiled/time_parse_binnedutc.vg.json b/examples/compiled/time_parse_binnedutc.vg.json index a739f18cb5d..55dc8f7d78d 100644 --- a/examples/compiled/time_parse_binnedutc.vg.json +++ b/examples/compiled/time_parse_binnedutc.vg.json @@ -16,7 +16,7 @@ {"type": "formula", "expr": "toDate(datum[\"date\"])", "as": "date"}, { "type": "formula", - "expr": "timeOffset('hours', datum['date'], 1)", + "expr": "utcOffset('hours', datum['date'], 1)", "as": "date_end" } ] diff --git a/examples/specs/bar_binned_yearmonth_grouped_center_band.vl.json b/examples/specs/bar_binned_yearmonth_grouped_center_band.vl.json new file mode 100644 index 00000000000..56c854dc14d --- /dev/null +++ b/examples/specs/bar_binned_yearmonth_grouped_center_band.vl.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "description": "Stock price over time.", + "data": {"url": "data/stocks.csv"}, + "transform": [ + {"filter": {"timeUnit": "binnedyearmonth", "field": "date", "range": [{"year": 2005, "month": 1}, {"year": 2005, "month": 3}]}} + ], + "mark": "bar", + "encoding": { + "x": {"timeUnit": "binnedyearmonth", "field": "date", "type": "temporal", "bandPosition": 0}, + "xOffset": {"field": "symbol", "type": "nominal"}, + "color": {"field": "symbol", "type": "nominal"}, + "y": {"field": "price", "type": "quantitative"} + } +} diff --git a/examples/specs/bar_binned_yearmonth_label_band_center.vl.json b/examples/specs/bar_binned_yearmonth_label_band_center.vl.json new file mode 100644 index 00000000000..798a6d11ecb --- /dev/null +++ b/examples/specs/bar_binned_yearmonth_label_band_center.vl.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "description": "Google's stock price over time.", + "data": {"url": "data/stocks.csv"}, + "transform": [ + {"filter": "datum.symbol==='GOOG'"}, + {"filter": {"timeUnit": "binnedyearmonth", "field": "date", "range": [{"year": 2005, "month": 1}, {"year": 2005, "month": 3}]}} + ], + "encoding": { + "x": {"timeUnit": "binnedyearmonth", "field": "date", "type": "temporal", "bandPosition": 0}, + "y": {"field": "price", "type": "quantitative"} + }, + "layer": [{ + "mark": "bar" + }, { + "mark": {"type": "text", "baseline": "bottom"}, + "encoding": { + "text": {"field": "price", "type": "quantitative"} + } + }] +} diff --git a/examples/specs/bar_month_temporal_band_center.vl.json b/examples/specs/bar_month_temporal_band_center.vl.json new file mode 100644 index 00000000000..d8ef4102c3b --- /dev/null +++ b/examples/specs/bar_month_temporal_band_center.vl.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "data": {"url": "data/seattle-weather.csv"}, + "mark": "bar", + "encoding": { + "x": {"timeUnit": "month", "field": "date", "type": "temporal", "bandPosition": 0}, + "y": {"aggregate": "mean", "field": "precipitation"} + } +} diff --git a/examples/specs/bar_month_temporal_band_center_config.vl.json b/examples/specs/bar_month_temporal_band_center_config.vl.json new file mode 100644 index 00000000000..7d8c05b4288 --- /dev/null +++ b/examples/specs/bar_month_temporal_band_center_config.vl.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "data": {"url": "data/seattle-weather.csv"}, + "mark": "bar", + "encoding": { + "x": {"timeUnit": "month", "field": "date", "type": "temporal"}, + "y": {"aggregate": "mean", "field": "precipitation"} + }, + "config": { + "bar": {"timeUnitBandPosition": 0} + } +} diff --git a/examples/specs/bar_yearmonth_center_band.vl.json b/examples/specs/bar_yearmonth_center_band.vl.json new file mode 100644 index 00000000000..69128eb98b9 --- /dev/null +++ b/examples/specs/bar_yearmonth_center_band.vl.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "description": "Temperature in Seattle as a bar chart with yearmonth time unit.", + "data": {"url": "data/seattle-weather.csv"}, + "mark": "bar", + "encoding": { + "x": {"timeUnit": "yearmonth", "field": "date", "bandPosition": 0}, + "y": {"aggregate": "mean", "field": "temp_max"} + } +} diff --git a/examples/specs/normalized/bar_binned_yearmonth_label_band_center_normalized.vl.json b/examples/specs/normalized/bar_binned_yearmonth_label_band_center_normalized.vl.json new file mode 100644 index 00000000000..7ab01797507 --- /dev/null +++ b/examples/specs/normalized/bar_binned_yearmonth_label_band_center_normalized.vl.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "description": "Google's stock price over time.", + "data": {"url": "data/stocks.csv"}, + "layer": [ + { + "mark": "bar", + "encoding": { + "x": { + "timeUnit": "binnedyearmonth", + "field": "date", + "type": "temporal", + "bandPosition": 0 + }, + "y": {"field": "price", "type": "quantitative"} + } + }, + { + "mark": {"type": "text", "baseline": "bottom"}, + "encoding": { + "x": { + "timeUnit": "binnedyearmonth", + "field": "date", + "type": "temporal", + "bandPosition": 0 + }, + "y": {"field": "price", "type": "quantitative"}, + "text": {"field": "price", "type": "quantitative"} + } + } + ], + "transform": [ + {"filter": "datum.symbol==='GOOG'"}, + { + "filter": { + "timeUnit": "binnedyearmonth", + "field": "date", + "range": [{"year": 2005, "month": 1}, {"year": 2005, "month": 3}] + } + } + ] +} \ No newline at end of file diff --git a/examples/specs/rect_heatmap_weather_temporal_center_band.vl.json b/examples/specs/rect_heatmap_weather_temporal_center_band.vl.json new file mode 100644 index 00000000000..5697820e156 --- /dev/null +++ b/examples/specs/rect_heatmap_weather_temporal_center_band.vl.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "data": { + "url": "data/seattle-weather.csv" + }, + "title": "Daily Max Temperatures (C) in Seattle, WA", + "config": { + "view": { + "strokeWidth": 0, + "step": 13 + }, + "axis": { + "domain": false + } + }, + "mark": "rect", + "encoding": { + "x": { + "field": "date", + "timeUnit": "date", + "type": "temporal", + "title": "Day", + "bandPosition": 0, + "axis": { + "labelAngle": 0, + "format": "%e" + } + }, + "y": { + "field": "date", + "timeUnit": "month", + "bandPosition": 0, + "type": "temporal", + "title": "Month" + }, + "color": { + "field": "temp_max", + "aggregate": "max", + "type": "quantitative", + "legend": { + "title": null + } + } + } +} diff --git a/examples/specs/rect_heatmap_weather_temporal_center_band_config.vl.json b/examples/specs/rect_heatmap_weather_temporal_center_band_config.vl.json new file mode 100644 index 00000000000..d00e44abbfe --- /dev/null +++ b/examples/specs/rect_heatmap_weather_temporal_center_band_config.vl.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "data": { + "url": "data/seattle-weather.csv" + }, + "title": "Daily Max Temperatures (C) in Seattle, WA", + "config": { + "view": { + "strokeWidth": 0, + "step": 13 + }, + "axis": { + "domain": false + }, + "rect": { + "timeUnitBandPosition": 0 + } + }, + "mark": "rect", + "encoding": { + "x": { + "field": "date", + "timeUnit": "date", + "type": "temporal", + "title": "Day", + "axis": { + "labelAngle": 0, + "format": "%e" + } + }, + "y": { + "field": "date", + "timeUnit": "month", + "type": "temporal", + "title": "Month" + }, + "color": { + "field": "temp_max", + "aggregate": "max", + "type": "quantitative", + "legend": { + "title": null + } + } + } +} diff --git a/site/_data/examples.json b/site/_data/examples.json index 94159777c3c..a1fab1f0064 100644 --- a/site/_data/examples.json +++ b/site/_data/examples.json @@ -84,6 +84,10 @@ "name": "bar_month_temporal_initial", "title": "Bar Chart showing Initials of Month Names" }, + { + "name": "bar_month_temporal_band_center", + "title": "Bar Chart with bars center-aligned with time unit ticks" + }, { "name": "bar_negative", "title": "Bar Chart with Negative Values and a Zero-Baseline" diff --git a/site/_includes/docs_toc.md b/site/_includes/docs_toc.md index 3e5e0662976..c1d1174988b 100644 --- a/site/_includes/docs_toc.md +++ b/site/_includes/docs_toc.md @@ -228,8 +228,8 @@ - [Documentation Overview]({{site.baseurl}}/docs/axis.html#documentation-overview) - [Axis Properties]({{site.baseurl}}/docs/axis.html#axis-properties) - [Axis Config]({{site.baseurl}}/docs/axis.html#config) - - [Band]({{site.baseurl}}/docs/band.html) - - [Examples]({{site.baseurl}}/docs/band.html#examples) + - [Band Position]({{site.baseurl}}/docs/bandposition.html) + - [Examples]({{site.baseurl}}/docs/bandposition.html#examples) - [Bin]({{site.baseurl}}/docs/bin.html) - [Documentation Overview]({{site.baseurl}}/docs/bin.html#documentation-overview) - [Binning in Encoding Field Definition]({{site.baseurl}}/docs/bin.html#encoding) diff --git a/site/_layouts/docs.html b/site/_layouts/docs.html index c46a28c6e04..5dc0b668485 100644 --- a/site/_layouts/docs.html +++ b/site/_layouts/docs.html @@ -98,8 +98,8 @@ url: aggregate - text: Axis url: axis - - text: Band - url: band + - text: Band Position + url: bandposition - text: Bin url: bin - text: Condition diff --git a/site/docs/encoding/band.md b/site/docs/encoding/band.md deleted file mode 100644 index e5d15520cd5..00000000000 --- a/site/docs/encoding/band.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -layout: docs -title: Band -permalink: /docs/band.html ---- - -Band properties can be used to adjust mark bandwidth or position for band scales, bin intervals, or time unit intervals. - -{% include table.html props="band" source="DatumDef" %} - -## Examples - -### Line Position - -By default, points in line marks are placed at the beginning of a time interval (e.g., "month"): - -

- -Setting `band` to `0.5` moves the points to the middle of the time interval. - -
diff --git a/site/docs/encoding/bandPosition.md b/site/docs/encoding/bandPosition.md new file mode 100644 index 00000000000..63a239c3968 --- /dev/null +++ b/site/docs/encoding/bandPosition.md @@ -0,0 +1,31 @@ +--- +layout: docs +title: Band Position +permalink: /docs/bandposition.html +--- + +Band properties can be used to adjust mark bandwidth or position for band scales, bin intervals, or time unit intervals. + +{% include table.html props="bandPosition" source="DatumDef" %} + +## Examples + +### Line Position + +By default, points in line marks are placed at the beginning of a time interval (e.g., "month"): + +
+ +Setting `bandPosition` to `0.5` moves the points to the middle of the time interval. + +
+ +### Bar Position + +By default, bar marks are placed from the beginning of a time interval (e.g., "month") to the end of the interval: + +
+ +Setting `bandPosition` to `0` moves the bar to center-align with ticks. + +
diff --git a/site/docs/transform/timeunit.md b/site/docs/transform/timeunit.md index 7f6ef48e6b4..1074cd956e4 100644 --- a/site/docs/transform/timeunit.md +++ b/site/docs/transform/timeunit.md @@ -68,6 +68,12 @@ Using `timeUnit` with rect-based marks (including `bar`, `rect`, and `image`) wi You can also add `"binned"` prefix if your data has already been binned and want Vega-Lite to apply the right formatting, including the right bands for the interval, to your charts. +By default, bar marks are placed from the beginning of a time interval (e.g., "month") to the end of the interval. + +Setting [`bandPosition`](bandposition.html) to `0` moves the bar to center-align with ticks. + +
+ ### Time Unit's Band diff --git a/src/channeldef.ts b/src/channeldef.ts index 77ed5a25811..7f2ccb8eb7d 100644 --- a/src/channeldef.ts +++ b/src/channeldef.ts @@ -534,7 +534,7 @@ export function getBandPosition({ if (isFieldDef(fieldDef)) { const {timeUnit, bin} = fieldDef; if (timeUnit && !fieldDef2) { - return isRectBasedMark(mark.type) ? 0 : getMarkConfig('timeUnitBandPosition', mark, config); + return getMarkConfig('timeUnitBandPosition', mark, config); } else if (isBinning(bin)) { return 0.5; } diff --git a/src/compile/data/aggregate.ts b/src/compile/data/aggregate.ts index b09238cf785..a8a9cefba11 100644 --- a/src/compile/data/aggregate.ts +++ b/src/compile/data/aggregate.ts @@ -5,9 +5,18 @@ import { getPositionChannelFromLatLong, getSecondaryRangeChannel, isGeoPositionChannel, - isScaleChannel + isScaleChannel, + isXorY } from '../../channel'; -import {binRequiresRange, FieldDef, hasBandEnd, isScaleFieldDef, isTypedFieldDef, vgField} from '../../channeldef'; +import { + binRequiresRange, + FieldDef, + getBandPosition, + hasBandEnd, + isScaleFieldDef, + isTypedFieldDef, + vgField +} from '../../channeldef'; import * as log from '../../log'; import {isFieldRange} from '../../scale'; import {AggregateTransform} from '../../transform'; @@ -15,6 +24,8 @@ import {Dict, duplicate, hash, keys, replacePathInField, setEqual} from '../../u import {isUnitModel, ModelWithField} from '../model'; import {UnitModel} from '../unit'; import {DataFlowNode} from './dataflow'; +import {isRectBasedMark} from '../../mark'; +import {OFFSETTED_RECT_END_SUFFIX, OFFSETTED_RECT_START_SUFFIX} from './timeunit'; type Measures = Dict>>>; @@ -29,6 +40,15 @@ function addDimension(dims: Set, channel: Channel, fieldDef: FieldDef { + const formula = model.reduceFieldDef((timeUnitComponent: TimeUnitComponent, fieldDef, channel) => { const {field, timeUnit} = fieldDef; if (timeUnit) { let component: TimeUnitComponent | undefined; + if (isBinnedTimeUnit(timeUnit)) { // For binned time unit, only produce end if the mark is a rect-based mark (rect, bar, image, arc), which needs "range". if (isUnitModel(model)) { - const {mark} = model; - if (isRectBasedMark(mark) || !!fieldDef.bandPosition) { + const {mark, markDef, config} = model; + const bandPosition = getBandPosition({fieldDef, markDef, config}); + if (isRectBasedMark(mark) || !!bandPosition) { component = { timeUnit: normalizeTimeUnit(timeUnit), field @@ -67,6 +72,15 @@ export class TimeUnitNode extends DataFlowNode { timeUnit }; } + + if (isUnitModel(model)) { + const {mark, markDef, config} = model; + const bandPosition = getBandPosition({fieldDef, markDef, config}); + if (isRectBasedMark(mark) && isXorY(channel) && bandPosition !== 0.5) { + component.rectBandPosition = bandPosition; + } + } + if (component) { timeUnitComponent[hash(component)] = component; } @@ -157,9 +171,14 @@ export class TimeUnitNode extends DataFlowNode { const transforms: (VgTimeUnitTransform | VgFormulaTransform)[] = []; for (const f of vals(this.timeUnits)) { + const {rectBandPosition} = f; + const normalizedTimeUnit = normalizeTimeUnit(f.timeUnit); + if (isTimeUnitTransformComponent(f)) { - const {field, as, timeUnit} = f; - const {unit, utc, ...params} = normalizeTimeUnit(timeUnit); + const {field, as} = f; + const {unit, utc, ...params} = normalizedTimeUnit; + + const startEnd: [string, string] = [as, `${as}_end`]; transforms.push({ field: replacePathInField(field), @@ -167,22 +186,76 @@ export class TimeUnitNode extends DataFlowNode { ...(unit ? {units: getTimeUnitParts(unit)} : {}), ...(utc ? {timezone: 'utc'} : {}), ...params, - as: [as, `${as}_end`] + as: startEnd }); + + transforms.push(...offsetedRectFormulas(startEnd, rectBandPosition, normalizedTimeUnit)); } else if (f) { - const {field: escapedField, timeUnit} = f; + const {field: escapedField} = f; // since this is a expression, we want the unescaped field name const field = escapedField.replaceAll('\\.', '.'); - const smallestUnit = getSmallestTimeUnitPart(timeUnit?.unit); - const {part, step} = getDateTimePartAndStep(smallestUnit, timeUnit.step); + const expr = offsetExpr({timeUnit: normalizedTimeUnit, field}); + const endAs = offsetAs(field); transforms.push({ type: 'formula', - expr: `timeOffset('${part}', datum['${field}'], ${step})`, - as: offsetAs(field) + expr, + as: endAs }); + + transforms.push(...offsetedRectFormulas([field, endAs], rectBandPosition, normalizedTimeUnit)); } } return transforms; } } + +export const OFFSETTED_RECT_START_SUFFIX = 'offsetted_rect_start'; +export const OFFSETTED_RECT_END_SUFFIX = 'offsetted_rect_end'; + +function offsetExpr({timeUnit, field, reverse}: {timeUnit: TimeUnitParams; field: string; reverse?: boolean}) { + const {unit, utc} = timeUnit; + const smallestUnit = getSmallestTimeUnitPart(unit); + const {part, step} = getDateTimePartAndStep(smallestUnit, timeUnit.step); + const offsetFn = utc ? 'utcOffset' : 'timeOffset'; + const expr = `${offsetFn}('${part}', datum['${field}'], ${reverse ? -step : step})`; + return expr; +} + +function offsetedRectFormulas( + [startField, endField]: [string, string], + rectBandPosition: number | undefined, + timeUnit: TimeUnitParams +): VgFormulaTransform[] { + if (rectBandPosition !== undefined && rectBandPosition !== 0.5) { + const startExpr = `datum['${startField}']`; + const endExpr = `datum['${endField}']`; + return [ + { + type: 'formula', + expr: interpolateExpr( + [ + offsetExpr({ + timeUnit, + field: startField, + reverse: true + }), + startExpr + ], + rectBandPosition + 0.5 + ), + as: `${startField}_${OFFSETTED_RECT_START_SUFFIX}` + }, + { + type: 'formula', + expr: interpolateExpr([startExpr, endExpr], rectBandPosition + 0.5), + as: `${startField}_${OFFSETTED_RECT_END_SUFFIX}` + } + ]; + } + return []; +} + +function interpolateExpr([start, end]: [string, string], fraction: number) { + return `${1 - fraction} * ${start} + ${fraction} * ${end}`; +} diff --git a/src/compile/mark/encode/position-rect.ts b/src/compile/mark/encode/position-rect.ts index c2da226aff3..afbd417d48d 100644 --- a/src/compile/mark/encode/position-rect.ts +++ b/src/compile/mark/encode/position-rect.ts @@ -11,7 +11,7 @@ import { PolarPositionChannel, PositionChannel } from '../../../channel'; -import {getBandSize, isFieldDef, isFieldOrDatumDef, TypedFieldDef, vgField} from '../../../channeldef'; +import {getBandPosition, getBandSize, isFieldDef, isFieldOrDatumDef, TypedFieldDef, vgField} from '../../../channeldef'; import {Config, getViewConfigDiscreteStep} from '../../../config'; import {Encoding} from '../../../encoding'; import * as log from '../../../log'; @@ -30,6 +30,7 @@ import * as ref from './valueref'; import {getOffsetScaleChannel} from '../../../channel'; import {getFirstDefined} from '../../../util'; import {Mark} from '../../../mark'; +import {OFFSETTED_RECT_END_SUFFIX, OFFSETTED_RECT_START_SUFFIX} from '../../data/timeunit'; export function rectPosition(model: UnitModel, channel: 'x' | 'y' | 'theta' | 'radius'): VgEncodeEntry { const {config, encoding, markDef} = model; @@ -331,13 +332,18 @@ function rectBinPosition({ ? (1 - bandSize.band) / 2 : 0.5; + const bandPosition = getBandPosition({fieldDef, fieldDef2, markDef, config}); + if (isBinning(fieldDef.bin) || fieldDef.timeUnit) { + const useRectOffsetField = fieldDef.timeUnit && bandPosition !== 0.5; + return { [vgChannel2]: rectBinRef({ fieldDef, scaleName, bandPosition: bandPositionForBandSize, - offset: binSpacingOffset2 + offset: binSpacingOffset2, + useRectOffsetField }), [vgChannel]: rectBinRef({ fieldDef, @@ -345,7 +351,8 @@ function rectBinPosition({ bandPosition: isSignalRef(bandPositionForBandSize) ? {signal: `1-${bandPositionForBandSize.signal}`} : 1 - bandPositionForBandSize, - offset: binSpacingOffset + offset: binSpacingOffset, + useRectOffsetField }) }; } else if (isBinned(fieldDef.bin)) { @@ -373,21 +380,29 @@ function rectBinPosition({ /** * Value Ref for binned fields */ -export function rectBinRef({ +function rectBinRef({ fieldDef, scaleName, bandPosition, - offset + offset, + useRectOffsetField }: { fieldDef: TypedFieldDef; scaleName: string; bandPosition: number | SignalRef; offset?: number | SignalRef; + useRectOffsetField: boolean; }) { return ref.interpolatedSignalRef({ scaleName, fieldOrDatumDef: fieldDef, bandPosition, - offset + offset, + ...(useRectOffsetField + ? { + startSuffix: OFFSETTED_RECT_START_SUFFIX, + endSuffix: OFFSETTED_RECT_END_SUFFIX + } + : {}) }); } diff --git a/src/compile/mark/encode/valueref.ts b/src/compile/mark/encode/valueref.ts index 2c427f54af9..7416b181558 100644 --- a/src/compile/mark/encode/valueref.ts +++ b/src/compile/mark/encode/valueref.ts @@ -167,12 +167,14 @@ export function interpolatedSignalRef({ fieldOrDatumDef2, offset, startSuffix, + endSuffix = 'end', bandPosition = 0.5 }: { scaleName: string; fieldOrDatumDef: TypedFieldDef; fieldOrDatumDef2?: SecondaryFieldDef; startSuffix?: string; + endSuffix?: string; offset: number | SignalRef | VgValueRef; bandPosition: number | SignalRef; }): VgValueRef { @@ -181,7 +183,7 @@ export function interpolatedSignalRef({ const end = fieldOrDatumDef2 !== undefined ? vgField(fieldOrDatumDef2, {expr}) - : vgField(fieldOrDatumDef, {suffix: 'end', expr}); + : vgField(fieldOrDatumDef, {suffix: endSuffix, expr}); const ref: VgValueRef = {}; diff --git a/src/compile/scale/domain.ts b/src/compile/scale/domain.ts index dd92b60cd35..1fde82c3268 100644 --- a/src/compile/scale/domain.ts +++ b/src/compile/scale/domain.ts @@ -13,6 +13,7 @@ import {isBinning, isBinParams, isParameterExtent} from '../../bin'; import {getSecondaryRangeChannel, isScaleChannel, ScaleChannel} from '../../channel'; import { binRequiresRange, + getBandPosition, getFieldOrDatumDef, hasBandEnd, isDatumDef, @@ -54,6 +55,8 @@ import {SignalRefWrapper} from '../signal'; import {Explicit, makeExplicit, makeImplicit, mergeValuesWithExplicit} from '../split'; import {UnitModel} from '../unit'; import {ScaleComponent, ScaleComponentIndex} from './component'; +import {isRectBasedMark} from '../../mark'; +import {OFFSETTED_RECT_END_SUFFIX, OFFSETTED_RECT_START_SUFFIX} from '../data/timeunit'; export function parseScaleDomain(model: Model) { if (isUnitModel(model)) { @@ -240,7 +243,7 @@ function parseSingleChannelDomain( model: UnitModel, channel: ScaleChannel | 'x2' | 'y2' ): Explicit { - const {encoding} = model; + const {encoding, markDef, mark, config, stack} = model; const fieldOrDatumDef = getFieldOrDatumDef(encoding[channel]) as ScaleDatumDef | ScaleFieldDef; const {type} = fieldOrDatumDef; @@ -258,7 +261,6 @@ function parseSingleChannelDomain( return makeExplicit(convertDomainIfItIsDateTime(domain, type, timeUnit)); } - const stack = model.stack; if (stack && channel === stack.fieldChannel) { if (stack.offset === 'normalize') { return makeImplicit([[0, 1]]); @@ -347,28 +349,27 @@ function parseSingleChannelDomain( ]); } } - } else if ( - fieldDef.timeUnit && - util.contains(['time', 'utc'], scaleType) && - hasBandEnd( - fieldDef, - isUnitModel(model) ? model.encoding[getSecondaryRangeChannel(channel)] : undefined, - model.markDef, - model.config - ) - ) { - const data = model.requestDataName(DataSourceType.Main); - return makeImplicit([ - { - data, - field: model.vgField(channel) - }, - { - data, - field: model.vgField(channel, {suffix: 'end'}) - } - ]); - } else if (sort) { + } else if (fieldDef.timeUnit && util.contains(['time', 'utc'], scaleType)) { + const fieldDef2 = encoding[getSecondaryRangeChannel(channel)]; + + if (hasBandEnd(fieldDef, fieldDef2, markDef, config)) { + const data = model.requestDataName(DataSourceType.Main); + + const bandPosition = getBandPosition({fieldDef, fieldDef2, markDef, config}); + const isRectWithOffset = isRectBasedMark(mark) && bandPosition !== 0.5; + return makeImplicit([ + { + data, + field: model.vgField(channel, isRectWithOffset ? {suffix: OFFSETTED_RECT_START_SUFFIX} : {}) + }, + { + data, + field: model.vgField(channel, {suffix: isRectWithOffset ? OFFSETTED_RECT_END_SUFFIX : 'end'}) + } + ]); + } + } + if (sort) { return makeImplicit([ { // If sort by aggregation of a specified sort field, we need to use RAW table, diff --git a/src/compile/scale/range.ts b/src/compile/scale/range.ts index 8267f21cbc5..90bcde77023 100644 --- a/src/compile/scale/range.ts +++ b/src/compile/scale/range.ts @@ -27,7 +27,14 @@ import { Y, YOFFSET } from '../../channel'; -import {getFieldOrDatumDef, isFieldDef, isFieldOrDatumDef, ScaleDatumDef, ScaleFieldDef} from '../../channeldef'; +import { + getBandPosition, + getFieldOrDatumDef, + isFieldDef, + isFieldOrDatumDef, + ScaleDatumDef, + ScaleFieldDef +} from '../../channeldef'; import {Config, getViewConfigDiscreteSize, getViewConfigDiscreteStep, ViewConfig} from '../../config'; import {DataSourceType} from '../../data'; import {channelHasFieldOrDatum} from '../../encoding'; @@ -379,6 +386,8 @@ function getOffsetRange(channel: string, model: UnitModel, offsetScaleType: Scal const positionScaleType = positionScaleCmpt.get('type'); const positionScaleName = model.scaleName(positionChannel); + const {markDef, config} = model; + if (positionScaleType === 'band') { const size = getDiscretePositionSize(positionChannel, model.size, model.config.view); @@ -397,9 +406,20 @@ function getOffsetRange(channel: string, model: UnitModel, offsetScaleType: Scal if (isFieldDef(positionDef) && positionDef.timeUnit) { const duration = durationExpr(positionDef.timeUnit, expr => `scale('${positionScaleName}', ${expr})`); const padding = model.config.scale.bandWithNestedOffsetPaddingInner; + const bandPositionOffset = + getBandPosition({ + fieldDef: positionDef, + markDef, + config + }) - 0.5; + const bandPositionOffsetExpr = bandPositionOffset !== 0 ? ` + ${bandPositionOffset}` : ''; if (padding) { - const startRatio = isSignalRef(padding) ? `${padding.signal}/2` : `${padding / 2}`; - const endRatio = isSignalRef(padding) ? `(1 - ${padding.signal}/2)` : `${1 - padding / 2}`; + const startRatio = isSignalRef(padding) + ? `${padding.signal}/2` + bandPositionOffsetExpr + : `${padding / 2 + bandPositionOffset}`; + const endRatio = isSignalRef(padding) + ? `(1 - ${padding.signal}/2)` + bandPositionOffsetExpr + : `${1 - padding / 2 + bandPositionOffset}`; return [{signal: `${startRatio} * (${duration})`}, {signal: `${endRatio} * (${duration})`}]; } return [0, {signal: duration}]; diff --git a/test/compile/data/aggregate.test.ts b/test/compile/data/aggregate.test.ts index e3b883490b4..b0d882152fa 100644 --- a/test/compile/data/aggregate.test.ts +++ b/test/compile/data/aggregate.test.ts @@ -1,4 +1,5 @@ import {AggregateNode} from '../../../src/compile/data/aggregate'; +import {OFFSETTED_RECT_END_SUFFIX, OFFSETTED_RECT_START_SUFFIX} from '../../../src/compile/data/timeunit'; import {AggregateTransform} from '../../../src/transform'; import {internalField} from '../../../src/util'; import {parseUnitModel} from '../../util'; @@ -83,6 +84,39 @@ describe('compile/data/aggregate', () => { }); }); + it('should produce the correct summary component for timeBinWithOffset', () => { + const model = parseUnitModel({ + mark: 'bar', + encoding: { + y: { + aggregate: 'sum', + field: 'Acceleration', + type: 'quantitative' + }, + x: { + timeUnit: 'yearmonth', + field: 'date', + type: 'temporal', + bandPosition: 0 + } + } + }); + + const agg = AggregateNode.makeFromEncoding(null, model); + expect(agg.assemble()).toEqual({ + type: 'aggregate', + groupby: [ + 'yearmonth_date', + 'yearmonth_date_end', + `yearmonth_date_${OFFSETTED_RECT_START_SUFFIX}`, + `yearmonth_date_${OFFSETTED_RECT_END_SUFFIX}` + ], + ops: ['sum'], + fields: ['Acceleration'], + as: ['sum_Acceleration'] + }); + }); + it('should produce the correct aggregate component for maps', () => { const model = parseUnitModel({ mark: 'rule', diff --git a/test/compile/data/timeunit.test.ts b/test/compile/data/timeunit.test.ts index 93524e74960..85cce49580e 100644 --- a/test/compile/data/timeunit.test.ts +++ b/test/compile/data/timeunit.test.ts @@ -1,4 +1,4 @@ -import {TimeUnitNode} from '../../../src/compile/data/timeunit'; +import {OFFSETTED_RECT_END_SUFFIX, OFFSETTED_RECT_START_SUFFIX, TimeUnitNode} from '../../../src/compile/data/timeunit'; import {ModelWithField} from '../../../src/compile/model'; import {TimeUnitTransform} from '../../../src/transform'; import {parseUnitModel} from '../../util'; @@ -52,6 +52,67 @@ describe('compile/data/timeunit', () => { ]); }); + it('should return a unit transform for bar with bandPosition=0', () => { + const model = parseUnitModel({ + data: {values: []}, + mark: 'bar', + encoding: { + x: {field: 'a', type: 'temporal', timeUnit: 'month', bandPosition: 0} + } + }); + + expect(assembleFromEncoding(model)).toEqual([ + { + type: 'timeunit', + field: 'a', + as: ['month_a', 'month_a_end'], + units: ['month'] + }, + { + type: 'formula', + expr: "0.5 * timeOffset('month', datum['month_a'], -1) + 0.5 * datum['month_a']", + as: `month_a_${OFFSETTED_RECT_START_SUFFIX}` + }, + + { + type: 'formula', + expr: "0.5 * datum['month_a'] + 0.5 * datum['month_a_end']", + as: `month_a_${OFFSETTED_RECT_END_SUFFIX}` + } + ]); + }); + + it('should return a timeunit transform with step for bar with bandPosition=0', () => { + const model = parseUnitModel({ + data: {values: []}, + mark: 'bar', + encoding: { + x: {field: 'a', type: 'temporal', timeUnit: {unit: 'month', step: 2}, bandPosition: 0} + } + }); + + expect(assembleFromEncoding(model)).toEqual([ + { + type: 'timeunit', + field: 'a', + as: ['month_step_2_a', 'month_step_2_a_end'], + units: ['month'], + step: 2 + }, + { + type: 'formula', + expr: "0.5 * timeOffset('month', datum['month_step_2_a'], -2) + 0.5 * datum['month_step_2_a']", + as: `month_step_2_a_${OFFSETTED_RECT_START_SUFFIX}` + }, + + { + type: 'formula', + expr: "0.5 * datum['month_step_2_a'] + 0.5 * datum['month_step_2_a_end']", + as: `month_step_2_a_${OFFSETTED_RECT_END_SUFFIX}` + } + ]); + }); + it('should return a unit offset transforms for bar', () => { const model = parseUnitModel({ data: {values: []}, @@ -70,6 +131,34 @@ describe('compile/data/timeunit', () => { ]); }); + it('should return a unit offset transforms for bar with bandPosition = 0', () => { + const model = parseUnitModel({ + data: {values: []}, + mark: 'bar', + encoding: { + x: {field: 'a', type: 'temporal', timeUnit: 'binnedyearmonth', bandPosition: 0} + } + }); + + expect(assembleFromEncoding(model)).toEqual([ + { + type: 'formula', + expr: `timeOffset('month', datum['a'], 1)`, + as: 'a_end' + }, + { + type: 'formula', + expr: "0.5 * timeOffset('month', datum['a'], -1) + 0.5 * datum['a']", + as: `a_${OFFSETTED_RECT_START_SUFFIX}` + }, + { + type: 'formula', + expr: "0.5 * datum['a'] + 0.5 * datum['a_end']", + as: `a_${OFFSETTED_RECT_END_SUFFIX}` + } + ]); + }); + it('should return the proper field escaping with binnedyearmonth', () => { const model = parseUnitModel({ data: {values: []}, diff --git a/test/compile/mark/encode/position-rect.test.ts b/test/compile/mark/encode/position-rect.test.ts index e5db3509476..a053a2d4d3f 100644 --- a/test/compile/mark/encode/position-rect.test.ts +++ b/test/compile/mark/encode/position-rect.test.ts @@ -1,3 +1,4 @@ +import {OFFSETTED_RECT_END_SUFFIX, OFFSETTED_RECT_START_SUFFIX} from '../../../../src/compile/data/timeunit'; import {rectPosition} from '../../../../src/compile/mark/encode/position-rect'; import * as log from '../../../../src/log'; import {parseUnitModelWithScaleAndLayoutSize} from '../../../util'; @@ -28,6 +29,25 @@ describe('compile/mark/encode/position-rect', () => { }); }); + it('produces correct x-mixins for timeUnit with bandPosition = 0', () => { + const model = parseUnitModelWithScaleAndLayoutSize({ + data: {values: []}, + mark: 'bar', + encoding: { + x: { + timeUnit: 'yearmonth', + field: 'date', + type: 'temporal', + bandPosition: 0 + } + } + }); + + const props = rectPosition(model, 'x'); + expect(props.x['field']).toBe(`yearmonth_date_${OFFSETTED_RECT_END_SUFFIX}`); + expect(props.x2['field']).toBe(`yearmonth_date_${OFFSETTED_RECT_START_SUFFIX}`); + }); + it('produces correct x-mixins for binned data with step and start field, without end field', () => { const model = parseUnitModelWithScaleAndLayoutSize({ data: {values: []}, diff --git a/test/compile/scale/domain.test.ts b/test/compile/scale/domain.test.ts index 4e068056362..afc4973fa82 100644 --- a/test/compile/scale/domain.test.ts +++ b/test/compile/scale/domain.test.ts @@ -8,6 +8,7 @@ import * as log from '../../../src/log'; import {ScaleType} from '../../../src/scale'; import {EncodingSortField} from '../../../src/sort'; import {parseUnitModel} from '../../util'; +import {OFFSETTED_RECT_END_SUFFIX, OFFSETTED_RECT_START_SUFFIX} from '../../../src/compile/data/timeunit'; describe('compile/scale', () => { describe('parseDomainForChannel()', () => { @@ -313,6 +314,43 @@ describe('compile/scale', () => { expect(_domain).toEqual([{data: 'main', field: 'month_origin'}]); }); + it('should return the correct bar domain for month T', () => { + const model = parseUnitModel({ + mark: 'bar', + encoding: { + y: { + field: 'origin', + type: 'temporal', + timeUnit: 'month' + } + } + }); + const _domain = testParseDomainForChannel(model, 'y'); + expect(_domain).toEqual([ + {data: 'main', field: 'month_origin'}, + {data: 'main', field: 'month_origin_end'} + ]); + }); + + it('should return the correct bar domain for month T with bandPosition = 0', () => { + const model = parseUnitModel({ + mark: 'bar', + encoding: { + y: { + field: 'origin', + type: 'temporal', + timeUnit: 'month', + bandPosition: 0 + } + } + }); + const _domain = testParseDomainForChannel(model, 'y'); + expect(_domain).toEqual([ + {data: 'main', field: `month_origin_${OFFSETTED_RECT_START_SUFFIX}`}, + {data: 'main', field: `month_origin_${OFFSETTED_RECT_END_SUFFIX}`} + ]); + }); + it('should return the correct domain for month O', () => { const model = parseUnitModel({ mark: 'point', diff --git a/test/compile/scale/range.test.ts b/test/compile/scale/range.test.ts index ed31590a8df..36cb40bec6d 100644 --- a/test/compile/scale/range.test.ts +++ b/test/compile/scale/range.test.ts @@ -132,7 +132,7 @@ describe('compile/scale', () => { ); }); - it('should return padded duration range when there is a nested offset with year time scale and no padding', () => { + it('should return padded duration range when there is a nested offset with year time scale and default padding', () => { const model = parseUnitModelWithScaleExceptRange({ mark: 'bar', encoding: { @@ -155,8 +155,31 @@ describe('compile/scale', () => { ]) ); }); + it('should return padded duration range when there is a nested offset with year time scale, default padding, and bandPosition=0', () => { + const model = parseUnitModelWithScaleExceptRange({ + mark: 'bar', + encoding: { + x: {field: 'x', type: 'temporal', timeUnit: 'year', bandPosition: 0}, + xOffset: {field: 'xSub', type: 'nominal'}, + y: {field: 'y', type: 'nominal'} + } + }); + + expect(parseRangeForChannel('xOffset', model)).toEqual( + makeImplicit([ + { + signal: + "-0.4 * (scale('x', datetime(2002, 0, 1, 0, 0, 0, 0)) - scale('x', datetime(2001, 0, 1, 0, 0, 0, 0)))" + }, + { + signal: + "0.4 * (scale('x', datetime(2002, 0, 1, 0, 0, 0, 0)) - scale('x', datetime(2001, 0, 1, 0, 0, 0, 0)))" + } + ]) + ); + }); - it('should return padded duration range signal when there is a nested offset with year time scale and no padding', () => { + it('should return padded duration range signal when there is a nested offset with year time scale and default padding', () => { const model = parseUnitModelWithScaleExceptRange({ mark: 'bar', encoding: {