Hi @PijushRoy
OK, here’s V2 with 1/4 and 1/2 hour displays and using the same colouring for weekends and weekdays:
This draft is essentially just:
- a title block
- a transform block (to calculate month, day, and hour values)
- a params block (to set the colours)
- a vconcat block for month title and weekly calendar
- in the weekly calendar block:
- a facet block (i.e., small multiples) with:
- days for columns
- hours for rows
- a layer block with:
- a “rect” mark for the full-height background
- a “rect” mark for the variable-height foreground
- a “text” mark for the task notes
Here’s the JSON specification code:
{
"title": {
"anchor": "start",
"align": "left",
"offset": -10,
"text": "eDNA Forum - Deneb Calendar - V2",
"font": "Verdana",
"fontSize": 36,
"fontWeight": "bold",
"fontStyle": "normal"
},
"data": {"name": "dataset"},
"transform": [
{
"calculate": "month(datum['DateTime'])",
"as": "_month"
},
{
"calculate": "monthFormat(datum['_month'])",
"as": "_month_name"
},
{
"calculate": "day(datum['DateTime'])",
"as": "_day_of_week"
},
{
"calculate": "timeFormat(datum['DateTime'], '%d')",
"as": "_day_of_month"
},
{
"calculate": "dayFormat(datum['_day_of_week'])",
"as": "_day_name"
},
{
"calculate": "datum['_day_name'] + '|' + datum['_day_of_month']",
"as": "_day_label"
},
{
"calculate": "hours(datum['DateTime'])",
"as": "_hour"
},
{
"calculate": "timeFormat(datum['DateTime'], '%I')",
"as": "_hour_padded"
},
{
"calculate": "timeFormat(datum['DateTime'], '%-I:%M %p')",
"as": "_hour_name"
},
{
"calculate": "datum['_hour_padded'] + '_' + datum['_hour_name']",
"as": "_hour_label"
},
{
"filter": {
"field": "_hour",
"range": [6, 18]
}
}
],
"params": [
{
"name": "_month_colour",
"value": "black"
},
{
"name": "_weekend_colour",
"value": "#E3E3E3"
},
{
"name": "_weekday_colour",
"value": "#E3E3E3"
},
{
"name": "_task_colour",
"value": "#35558A"
},
{
"name": "_text_colour",
"value": "white"
}
],
"vconcat": [
{
"name": "MONTH_TITLE",
"width": 180,
"height": 50,
"transform": [
{
"joinaggregate": [
{
"op": "min",
"field": "__row__",
"as": "_min_row_number"
}
]
},
{
"filter": "datum['__row__'] == datum['_min_row_number']"
}
],
"mark": {
"type": "text",
"color": {
"expr": "_month_colour"
},
"align": "left",
"fontSize": 24,
"fontWeight": "bold",
"x": 0,
"y": 40
},
"encoding": {
"text": {
"field": "_month_name",
"type": "nominal"
}
}
},
{
"name": "CALENDAR",
"spacing": 2,
"facet": {
"column": {
"field": "_day_label",
"sort": {
"field": "_day",
"type": "quantitative",
"order": "ascending"
},
"header": {
"lineBreak": "|",
"labelLineHeight": 16,
"labelAnchor": "start",
"labelExpr": "[slice(datum.value, 0, length(datum.value)-3), slice(datum.value, -2, 100)]",
"labelFontSize": 12,
"title": false
}
},
"row": {
"field": "_hour_label",
"sort": {
"field": "_hour",
"type": "quantitative",
"order": "ascending"
},
"header": {
"labelAngle": 0,
"labelAlign": "left",
"labelExpr": "slice(datum.value, 3, 100)",
"labelFontSize": 12,
"title": false
}
}
},
"spec": {
"layer": [
{
"name": "BACKGROUND",
"width": 160,
"height": 48,
"mark": {"type": "rect"},
"encoding": {
"color": {
"condition": [
{
"test": {
"field": "_day_of_week",
"oneOf": [0, 6]
},
"value": {
"expr": "_weekend_colour"
}
}
],
"value": {
"expr": "_weekday_colour"
}
}
}
},
{
"name": "FOREGROUND",
"width": 120,
"height": 48,
"transform": [
{
"filter": {
"field": "Task ID Measure",
"gt": 0
}
},
{
"calculate": "( datum['Task End DateTime'] - datum['Task Start DateTime'] ) / (60 * 60 * 1000 )",
"as": "_duration_in_hours"
}
],
"mark": {
"type": "rect",
"color": {
"expr": "_task_colour"
},
"stroke": "white"
},
"encoding": {
"y": {
"datum": 100,
"type": "quantitative",
"scale": {
"domain": [0, 100]
},
"axis": null
},
"y2": {"datum": { "expr": "datum['_duration_in_hours'] == 0.25 ? 75 : datum['_duration_in_hours'] == 0.50 ? 50: datum['_duration_in_hours'] == 0.75 ? 25 : 0" }}
}
},
{
"name": "FOREGROUND_TEXT",
"width": 180,
"height": 50,
"transform": [
{
"filter": {
"field": "Task ID Measure",
"gt": 0
}
},
{
"calculate": "timeFormat(datum['Task Start DateTime'], '%-I:%M %p')",
"as": "_start_hour_name"
},
{
"calculate": "timeFormat(datum['Task End DateTime'], '%-I:%M %p')",
"as": "_end_hour_name"
},
{
"calculate": "datum['_start_hour_name'] + '-' +datum['_end_hour_name'] + '|' + datum['Task Notes Measure']",
"as": "_multiline_task_label"
},
{
"calculate": "datum['Task Notes Measure']",
"as": "_task_label"
}
],
"mark": {
"type": "text",
"align": "left",
"lineBreak": "|",
"xOffset": -78,
"yOffset": -17,
"limit": 160,
"color": {
"expr": "_text_colour"
}
},
"encoding": {
"text": {
"field": "_task_label"
}
}
}
]
}
}
]
}
This is still rough, and further development would be warranted (for example, it doesn’t properly handle situations where there are multiple events in a single 1-hour interval), but should be a useful start.
Hope it helps.
Greg
eDNA Forum - Deneb Calendar - V2.pbix (1.5 MB)
Tasks.xlsx (9.5 KB)