Textract JSON data to Image drawing with Kendo UI Angular 11 - html

I have a JSON object which was extracted from an Image using AWS Textract after that I'm trying to draw same image using this textract data.
I'm trying to use Kendo Angular Drawing API for this and with limited documentation and usage examples I'm not completely sure about how to implement this from the geometry values in the JSON
Sample extracted object
{
"BlockType": "WORD",
"Confidence": 99.93840789794922,
"Text": "The",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.036412131041288376,
"Height": 0.014151308685541153,
"Left": 0.13431107997894287,
"Top": 0.03131059184670448
},
"Polygon": [
{
"X": 0.13431107997894287,
"Y": 0.03131059184670448
},
{
"X": 0.17072321474552155,
"Y": 0.03131059184670448
},
{
"X": 0.17072321474552155,
"Y": 0.045461900532245636
},
{
"X": 0.13431107997894287,
"Y": 0.045461900532245636
}
]
},
"Id": "da6d85a6-3c79-484b-a07d-d84e968844ae",
"Page": 1,
"SearchKey": "The"
},
{
"BlockType": "WORD",
"Confidence": 99.77457427978516,
"Text": "Standard",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.08427372574806213,
"Height": 0.013822372071444988,
"Left": 0.17733719944953918,
"Top": 0.03183155879378319
},
"Polygon": [
{
"X": 0.17733719944953918,
"Y": 0.03183155879378319
},
{
"X": 0.2616109251976013,
"Y": 0.03183155879378319
},
{
"X": 0.2616109251976013,
"Y": 0.04565393179655075
},
{
"X": 0.17733719944953918,
"Y": 0.04565393179655075
}
]
},
"Id": "e6587cca-2e7e-4231-9978-4b303241645b",
"Page": 1,
"SearchKey": "Standard"
},
{
"BlockType": "WORD",
"Confidence": 99.82964324951172,
"Text": "Bill",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.035674601793289185,
"Height": 0.014618489891290665,
"Left": 0.26750874519348145,
"Top": 0.03160806745290756
},
"Polygon": [
{
"X": 0.26750874519348145,
"Y": 0.03160806745290756
},
{
"X": 0.30318334698677063,
"Y": 0.03160806745290756
},
{
"X": 0.30318334698677063,
"Y": 0.04622655734419823
},
{
"X": 0.26750874519348145,
"Y": 0.04622655734419823
}
]
},
"Id": "a6947531-ae08-40b3-8161-8fa78d430ad4",
"Page": 1,
"SearchKey": "Bill"
},
{
"BlockType": "WORD",
"Confidence": 99.97029876708984,
"Text": "of",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.022517522796988487,
"Height": 0.014790677465498447,
"Left": 0.3098187744617462,
"Top": 0.03196505829691887
},
"Polygon": [
{
"X": 0.3098187744617462,
"Y": 0.03196505829691887
},
{
"X": 0.33233630657196045,
"Y": 0.03196505829691887
},
{
"X": 0.33233630657196045,
"Y": 0.04675573855638504
},
{
"X": 0.3098187744617462,
"Y": 0.04675573855638504
}
]
},
"Id": "53853140-e3c4-4f3a-a4b7-67ad39e1bb32",
"Page": 1,
"SearchKey": "of"
},
{
"BlockType": "WORD",
"Confidence": 99.88966369628906,
"Text": "Lading",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.06747607886791229,
"Height": 0.01772529073059559,
"Left": 0.3355117440223694,
"Top": 0.032798975706100464
},
"Polygon": [
{
"X": 0.3355117440223694,
"Y": 0.032798975706100464
},
{
"X": 0.40298783779144287,
"Y": 0.032798975706100464
},
{
"X": 0.40298783779144287,
"Y": 0.0505242682993412
},
{
"X": 0.3355117440223694,
"Y": 0.0505242682993412
}
]
},
"Id": "5060d908-2318-48ad-bd6c-e4694501a8ce",
"Page": 1,
"SearchKey": "Lading"
},
{
"BlockType": "WORD",
"Confidence": 99.94490814208984,
"Text": "Form",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.051753073930740356,
"Height": 0.014337458647787571,
"Left": 0.4095679521560669,
"Top": 0.033189330250024796
},
"Polygon": [
{
"X": 0.4095679521560669,
"Y": 0.033189330250024796
},
{
"X": 0.46132102608680725,
"Y": 0.033189330250024796
},
{
"X": 0.46132102608680725,
"Y": 0.04752678796648979
},
{
"X": 0.4095679521560669,
"Y": 0.04752678796648979
}
]
},
"Id": "3cf3fb15-e77f-48a5-bf76-be35d32f8be6",
"Page": 1,
"SearchKey": "Form"
},
Canvas drawing is completely new to me and I'm little lost about this implementation
Any recommendations will help me thanks

The retrieved data from AWS Textract are not exactly images.
The simplest way to say it is that they are blocks of recognized text, as stated at their webpage:
Amazon Textract makes it easy to add document text detection and
analysis to your applications. The Amazon Textract Text Detection API
can detect typed and handwritten text in a variety of documents
including financial reports, medical records, and tax forms. For
documents with structured data, you can use the Amazon Textract
Document Analysis API to extract text, forms and tables. Furthermore,
if you would like to process invoices and receipts, you can use the
AnalyzeExpense API.
Those blocks are the results of the text-detection operation. A Block represents items that are recognized in a document within a group of pixels close to each other (see more here). They only represent something for the input you've sent to the API.
I've made the following snippet which does what you ask. However, the gathered response from the API is meant to be used as your implementation needs; and may not be the following way:
var data = [{
"BlockType": "WORD",
"Confidence": 99.93840789794922,
"Text": "The",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.036412131041288376,
"Height": 0.014151308685541153,
"Left": 0.13431107997894287,
"Top": 0.03131059184670448
},
"Polygon": [
{
"X": 0.13431107997894287,
"Y": 0.03131059184670448
},
{
"X": 0.17072321474552155,
"Y": 0.03131059184670448
},
{
"X": 0.17072321474552155,
"Y": 0.045461900532245636
},
{
"X": 0.13431107997894287,
"Y": 0.045461900532245636
}
]
},
"Id": "da6d85a6-3c79-484b-a07d-d84e968844ae",
"Page": 1,
"SearchKey": "The"
},
{
"BlockType": "WORD",
"Confidence": 99.77457427978516,
"Text": "Standard",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.08427372574806213,
"Height": 0.013822372071444988,
"Left": 0.17733719944953918,
"Top": 0.03183155879378319
},
"Polygon": [
{
"X": 0.17733719944953918,
"Y": 0.03183155879378319
},
{
"X": 0.2616109251976013,
"Y": 0.03183155879378319
},
{
"X": 0.2616109251976013,
"Y": 0.04565393179655075
},
{
"X": 0.17733719944953918,
"Y": 0.04565393179655075
}
]
},
"Id": "e6587cca-2e7e-4231-9978-4b303241645b",
"Page": 1,
"SearchKey": "Standard"
},
{
"BlockType": "WORD",
"Confidence": 99.82964324951172,
"Text": "Bill",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.035674601793289185,
"Height": 0.014618489891290665,
"Left": 0.26750874519348145,
"Top": 0.03160806745290756
},
"Polygon": [
{
"X": 0.26750874519348145,
"Y": 0.03160806745290756
},
{
"X": 0.30318334698677063,
"Y": 0.03160806745290756
},
{
"X": 0.30318334698677063,
"Y": 0.04622655734419823
},
{
"X": 0.26750874519348145,
"Y": 0.04622655734419823
}
]
},
"Id": "a6947531-ae08-40b3-8161-8fa78d430ad4",
"Page": 1,
"SearchKey": "Bill"
},
{
"BlockType": "WORD",
"Confidence": 99.97029876708984,
"Text": "of",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.022517522796988487,
"Height": 0.014790677465498447,
"Left": 0.3098187744617462,
"Top": 0.03196505829691887
},
"Polygon": [
{
"X": 0.3098187744617462,
"Y": 0.03196505829691887
},
{
"X": 0.33233630657196045,
"Y": 0.03196505829691887
},
{
"X": 0.33233630657196045,
"Y": 0.04675573855638504
},
{
"X": 0.3098187744617462,
"Y": 0.04675573855638504
}
]
},
"Id": "53853140-e3c4-4f3a-a4b7-67ad39e1bb32",
"Page": 1,
"SearchKey": "of"
},
{
"BlockType": "WORD",
"Confidence": 99.88966369628906,
"Text": "Lading",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.06747607886791229,
"Height": 0.01772529073059559,
"Left": 0.3355117440223694,
"Top": 0.032798975706100464
},
"Polygon": [
{
"X": 0.3355117440223694,
"Y": 0.032798975706100464
},
{
"X": 0.40298783779144287,
"Y": 0.032798975706100464
},
{
"X": 0.40298783779144287,
"Y": 0.0505242682993412
},
{
"X": 0.3355117440223694,
"Y": 0.0505242682993412
}
]
},
"Id": "5060d908-2318-48ad-bd6c-e4694501a8ce",
"Page": 1,
"SearchKey": "Lading"
},
{
"BlockType": "WORD",
"Confidence": 99.94490814208984,
"Text": "Form",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.051753073930740356,
"Height": 0.014337458647787571,
"Left": 0.4095679521560669,
"Top": 0.033189330250024796
},
"Polygon": [
{
"X": 0.4095679521560669,
"Y": 0.033189330250024796
},
{
"X": 0.46132102608680725,
"Y": 0.033189330250024796
},
{
"X": 0.46132102608680725,
"Y": 0.04752678796648979
},
{
"X": 0.4095679521560669,
"Y": 0.04752678796648979
}
]
},
"Id": "3cf3fb15-e77f-48a5-bf76-be35d32f8be6",
"Page": 1,
"SearchKey": "Form"
},
{
"BlockType": "WORD",
"Confidence": 99.93840789794922,
"Text": "The",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.036412131041288376,
"Height": 0.014151308685541153,
"Left": 0.13431107997894287,
"Top": 0.03131059184670448
},
"Polygon": [
{
"X": 0.13431107997894287,
"Y": 0.03131059184670448
},
{
"X": 0.17072321474552155,
"Y": 0.03131059184670448
},
{
"X": 0.17072321474552155,
"Y": 0.045461900532245636
},
{
"X": 0.13431107997894287,
"Y": 0.045461900532245636
}
]
},
"Id": "da6d85a6-3c79-484b-a07d-d84e968844ae",
"Page": 1,
"SearchKey": "The"
},
{
"BlockType": "WORD",
"Confidence": 99.77457427978516,
"Text": "Standard",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.08427372574806213,
"Height": 0.013822372071444988,
"Left": 0.17733719944953918,
"Top": 0.03183155879378319
},
"Polygon": [
{
"X": 0.17733719944953918,
"Y": 0.03183155879378319
},
{
"X": 0.2616109251976013,
"Y": 0.03183155879378319
},
{
"X": 0.2616109251976013,
"Y": 0.04565393179655075
},
{
"X": 0.17733719944953918,
"Y": 0.04565393179655075
}
]
},
"Id": "e6587cca-2e7e-4231-9978-4b303241645b",
"Page": 1,
"SearchKey": "Standard"
},
{
"BlockType": "WORD",
"Confidence": 99.82964324951172,
"Text": "Bill",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.035674601793289185,
"Height": 0.014618489891290665,
"Left": 0.26750874519348145,
"Top": 0.03160806745290756
},
"Polygon": [
{
"X": 0.26750874519348145,
"Y": 0.03160806745290756
},
{
"X": 0.30318334698677063,
"Y": 0.03160806745290756
},
{
"X": 0.30318334698677063,
"Y": 0.04622655734419823
},
{
"X": 0.26750874519348145,
"Y": 0.04622655734419823
}
]
},
"Id": "a6947531-ae08-40b3-8161-8fa78d430ad4",
"Page": 1,
"SearchKey": "Bill"
},
{
"BlockType": "WORD",
"Confidence": 99.97029876708984,
"Text": "of",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.022517522796988487,
"Height": 0.014790677465498447,
"Left": 0.3098187744617462,
"Top": 0.03196505829691887
},
"Polygon": [
{
"X": 0.3098187744617462,
"Y": 0.03196505829691887
},
{
"X": 0.33233630657196045,
"Y": 0.03196505829691887
},
{
"X": 0.33233630657196045,
"Y": 0.04675573855638504
},
{
"X": 0.3098187744617462,
"Y": 0.04675573855638504
}
]
},
"Id": "53853140-e3c4-4f3a-a4b7-67ad39e1bb32",
"Page": 1,
"SearchKey": "of"
},
{
"BlockType": "WORD",
"Confidence": 99.88966369628906,
"Text": "Lading",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.06747607886791229,
"Height": 0.01772529073059559,
"Left": 0.3355117440223694,
"Top": 0.032798975706100464
},
"Polygon": [
{
"X": 0.3355117440223694,
"Y": 0.032798975706100464
},
{
"X": 0.40298783779144287,
"Y": 0.032798975706100464
},
{
"X": 0.40298783779144287,
"Y": 0.0505242682993412
},
{
"X": 0.3355117440223694,
"Y": 0.0505242682993412
}
]
},
"Id": "5060d908-2318-48ad-bd6c-e4694501a8ce",
"Page": 1,
"SearchKey": "Lading"
},
{
"BlockType": "WORD",
"Confidence": 99.94490814208984,
"Text": "Form",
"TextType": "PRINTED",
"Geometry": {
"BoundingBox": {
"Width": 0.051753073930740356,
"Height": 0.014337458647787571,
"Left": 0.4095679521560669,
"Top": 0.033189330250024796
},
"Polygon": [
{
"X": 0.4095679521560669,
"Y": 0.033189330250024796
},
{
"X": 0.46132102608680725,
"Y": 0.033189330250024796
},
{
"X": 0.46132102608680725,
"Y": 0.04752678796648979
},
{
"X": 0.4095679521560669,
"Y": 0.04752678796648979
}
]
},
"Id": "3cf3fb15-e77f-48a5-bf76-be35d32f8be6",
"Page": 1,
"SearchKey": "Form"
}
];
const zoom = 1400;
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const drawer = {
BoundingBox: drawBoundingBox,
Polygon: drawPolygon,
};
ctx.strokeStyle = "#0000ff";
ctx.lineWidth = 1;
data.forEach((args) => {
if (args.Geometry) {
for (var key in args.Geometry) {
if (typeof drawer[key] === 'function') {
drawer[key](args.Geometry[key], args.Text);
} else {
console.error('Unsupported object, you\'ll need to add support here!');
}
}
}
});
function drawBoundingBox(args, text) {
ctx.beginPath();
ctx.rect(args.Left * zoom, args.Top * zoom, args.Width * zoom, args.Height * zoom);
ctx.stroke();
ctx.font = (args.Height * zoom) + "px Arial";
ctx.fillText(text, (args.Left * zoom), (args.Top * zoom) + (args.Height * zoom));
}
function drawPolygon(args, text) {
ctx.beginPath();
ctx.moveTo(args[0].X * zoom, args[0].Y * zoom);
for (var i = 1; i < args.length; i++) {
ctx.lineTo(args[i].X * zoom, args[i].Y * zoom);
}
ctx.lineTo(args[0].X * zoom, args[0].Y * zoom);
ctx.stroke();
const h = ((args[args.length - 1].Y - args[0].Y) * zoom);
ctx.font = h + "px Arial";
ctx.strokeText(text, args[0].X * zoom, (args[0].Y + h) * zoom);
}
<canvas width="850" height="450" id="canvas"></canvas>
Canvas Drawing
This is the output for the example JSON data you've provided by using the snippet code:
Canvas drawing is not that hard. To code the snippet I've shared to you, you only need knowledge on 3 subjects (Text, Rect and lineTo)
Kendo Angular Drawing
I've ported the vanilla code to Kendo Angular Drawing in this stackblitz sample, I'd like to let you know that vanilla is a lot easier to use than Kendo, this is the output:
In both cases, canvas and kendo; I had to add a zoom variable because your X, Y values were too small.

Related

How to add text on bar chart in vega lite

I am trying to add text label to bar chart, but it not showing it. Here is a example i want to achieve
Here is code
{ "$schema": "https://vega.github.io/schema/vega-lite/v5.json", "background": "#EFF1EF", "width": 500, "height": 26, "autosize":"pad", "data": { "values": [ { "standarts": { "divisions": [ { "a": { "mean": 135, "part1": 10, "part2": 60, "part5": 130,
"part7": 198, "part9": 255, "goal": { "value": 150 } } } ] } } ] }, "transform": [ {"calculate": "datum.standarts.divisions", "as": "D"}, {"flatten": ["D"]}, {"calculate": "datum.D.a", "as": "x"} ], "encoding": { "x": {"field": "x", "type": "quantitative","scale":{"domain":[0,300]},
"stack": null}, "opacity": {"value": 1} }, "layer": [ { "mark":{"type":"bar", "color" : "#b2b7b4", "cornerRadius": 40}, "encoding": {"x": {}} }, { "mark": {"type": "bar", "color": "#0ef9e5", "height": 25}, "encoding": {"x": {"field": "x.mean"}} }, { "mark":{"type":
"text", "align":"center"}, "encoding": {"text": {"field": "x.mean"}} }, { "mark":{"type": "rule"}, "encoding": {"x": {"field":"x.goal.value", "title":["Target"],"axis":{ "titleFont": "Google Sans", "titleFontSize": 16, "titleFontWeight":700, "titleY":
8} }}} ], "config": { "axis": {"ticks": false, "labels": false, "domain": false}, "rule": { "strokeWidth": 4, "strokeDash": [ 0.2, 8 ], "strokeCap": "round", "align":"center" } } }
this is code
And link to editor
vega-lite editor
text part is still not working.
I used documentation on vega-lite website and example codes, but still no success.
Thanks for the help.
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"background": "#EFF1EF",
"width": 500,
"height": 26,
"autosize": "pad",
"data": {
"values": [
{
"standarts": {
"divisions": [
{
"a": {
"mean": 135,
"part1": 10,
"part2": 60,
"part5": 130,
"part7": 198,
"part9": 255,
"goal": {"value": 150}
}
}
]
}
}
]
},
"transform": [
{"calculate": "datum.standarts.divisions", "as": "D"},
{"flatten": ["D"]},
{"calculate": "datum.D.a", "as": "x"}
],
"encoding": {
"x": {
"field": "x",
"type": "quantitative",
"scale": {"domain": [0, 300]},
"stack": null
},
"opacity": {"value": 1}
},
"layer": [
{
"mark": {"type": "bar", "color": "#b2b7b4", "cornerRadius": 40},
"encoding": {"x": {}}
},
{
"mark": {"type": "bar", "color": "#0ef9e5", "height": 25},
"encoding": {"x": {"field": "x.mean"}}
},
{
"mark": {"type": "text", "align": "right", "dx": -10},
"encoding": {"text": {"field": "x.mean"}, "x": {"field": "x.mean"}}
},
{
"mark": {"type": "text", "align": "center", "dy": -30},
"encoding": {
"text": {"field": "x.goal.value"},
"x": {"field": "x.goal.value"}
}
},
{
"mark": {"type": "rule"},
"encoding": {
"x": {
"field": "x.goal.value",
"title": ["Target"],
"axis": {
"titleFont": "Google Sans",
"titleFontSize": 16,
"titleFontWeight": 700,
"titleY": 8
}
}
}
}
],
"config": {
"axis": {"ticks": false, "labels": false, "domain": false},
"rule": {
"strokeWidth": 4,
"strokeDash": [0.2, 8],
"strokeCap": "round",
"align": "center"
}
}
}

vega lite - persist null date/time in chart

How do I persist the null in the data on visualization with vega chart while rendering the date/time for data the timestamp. Right now, it looks like null data is getting passed as default time of 04:00
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"values": [
{
"x": null,
"y": "recoPU8F3Asuc8t3n"
},
{
"x": "2017-06-02T07:00:00.000Z",
"y": "reco056sZzsAd7qVp"
},
{
"x": null,
"y": "reclQZbw7CiAegm1f"
},
{
"x": "2017-07-14T07:00:00.000Z",
"y": "recAzUhUdekIdkSVt"
},
{
"x": null,
"y": "rec0NHCpaRDP9W1pd"
},
{
"x": null,
"y": "recPYz3QNpXgrmBVy"
}
]
},
"config": {
"range": {
"category": [
"#46bfdd"
]
},
"legend": {
"disable": false,
"labelColor": "#333",
"labelFontSize": 11,
"symbolSize": 30,
"rowPadding": 4,
"symbolType": "circle",
"symbolStrokeWidth": 0,
"titleColor": "#666666",
"titleFontSize": 11,
"titlePadding": 10,
"titleFontWeight": 500,
"orient": "right"
},
"axisY": {
"tickMinStep": 1,
"format": "~s",
"formatType": "number"
},
"axis": {
"domainColor": "#E8E8E8",
"gridColor": "#E8E8E8",
"tickColor": "#E8E8E8",
"grid": true,
"gridWidth": 1,
"labelColor": "#666666",
"labelFontSize": 11,
"labelLimit": 180,
"titleColor": "#999999",
"tickSize": 16,
"titleFontSize": 13,
"titlePadding": 10,
"titleFontWeight": 400,
"labelPadding": 4,
"titleLimit": 180
},
"autosize": {
"type": "fit",
"contains": "padding"
},
"axisLeft": {
"labelFontSize": 11,
"labelColor": "#999999",
"labelLimit": 180,
"titleFontSize": 13,
"titleFontWeight": 400,
"titleLimit": 180
},
"style": {
"label": {
"align": "left",
"baseline": "middle",
"dx": 4
},
"cell": {
"stroke": "transparent"
}
}
},
"mark": {
"type": "bar",
"width": {
"band": 0.7
},
"tooltip": true,
"cornerRadiusEnd": 3,
"cursor": "pointer",
"color": "#4787cd"
},
"params": [
{
"name": "highlight",
"select": {
"type": "point",
"on": "mouseover"
}
},
{
"name": "select",
"select": "point"
}
],
"encoding": {
"x": {
"field": "x",
"type": "nominal",
"axis": {
"labelAngle": -90,
"format": "%I:%M",
"formatType": "time",
"title": "Reservation On"
},
"sort": {
"encoding": "x",
"order": "ascending"
},
"timeUnit": "hoursminutes"
},
"y": {
"field": "y",
"type": "quantitative",
"aggregate": "count",
"axis": {
"title": "Number of records"
}
},
"tooltip": [
{
"field": "x",
"title": "Reservation On",
"format": "%I:%M",
"formatType": "time",
"timeUnit": "hoursminutes"
},
{
"field": "y",
"type": "quantitative",
"aggregate": "count",
"title": "Number of records"
}
],
"fillOpacity": {
"condition": [
{
"param": "select",
"value": 1
}
],
"value": 0.3
},
"strokeWidth": {
"condition": [
{
"param": "select",
"empty": false,
"value": 2
},
{
"param": "highlight",
"empty": false,
"value": 1
}
],
"value": 0
}
},
"transform": [
],
"height": 372,
"width": 536
}
One way to do this is by replacing your "format" with an appropriate "labelExpr":
"axis": {
"labelAngle": -90,
"labelExpr": "isValid(datum.value) ? timeFormat(datum.value, '%I:%M') : 'null'",
"title": "Reservation On"
}

label bar chart in Vega Lite with both absolute number and percentage

I am able to label a bar chart created in Vega Lite, with either its percentage of the total or its absolute number, like so:
And here's the spec:
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"width": "container",
"height": "container",
"data": {
"values": [
{
"binMin": -300,
"binMax": 0,
"binName": "-5 to 0 minutes",
"count": 29,
"percentOfTotal": 0.14215686274509803
},
{
"binMin": 0,
"binMax": 360,
"binName": "0 to +6 minutes",
"count": 51,
"percentOfTotal": 0.25
},
{
"binMin": 360,
"binMax": 600,
"binName": "+6 to +10 minutes",
"count": 32,
"percentOfTotal": 0.1568627450980392
},
{
"binMin": 600,
"binMax": 1200,
"binName": "+10 to +20 minutes",
"count": 90,
"percentOfTotal": 0.4411764705882353
}
]
},
"layer": [
{"mark": "bar"},
{
"mark": {
"type": "text",
"align": "center",
"yOffset": -10,
"fontSize": 18
},
"encoding": {
"text": {
"field": "percentOfTotal",
"type": "quantitative",
"format": ".0%",
"formatType": "number"
}
}
}
],
"encoding": {
"x": {
"field": "binName",
"type": "nominal",
"title": "Wait Time for Transfer (Minutes)",
"axis": {"titleFontSize": 18, "labelAngle": 0, "labelFontSize": 14},
"sort": {"field": "binMin"}
},
"y": {
"field": "count",
"type": "quantitative",
"title": "Total Transfers",
"axis": {"titleFontSize": 18, "labelFontSize": 14}
},
"color": {
"legend": false,
"field": "binMin",
"scale": {"range": ["red", "orange", "green", "yellowgreen"]}
}
}
}
I would like to change the label to display both, like a label for the left/red bar of "29 (14%)".
I have figured out how to set config.customFormatTypes = true and to write a little formatter function in JavaScript. However, I am unclear how I would be able to pass two fields into this formatter, in order to pass it both the absolute number and the percentage (or the absolute number for that category and the total number across all categories).
Any pointers? Thanks.
To display the concatenated value of 2 fields "29 (14%)" over text you can use transform calculate and generate a concatenated string based on your field. Then, use that field in your text mark as done below or refer editor:
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"width": "container",
"height": "container",
"data": {
"values": [
{
"binMin": -300,
"binMax": 0,
"binName": "-5 to 0 minutes",
"count": 29,
"percentOfTotal": 0.14215686274509803
},
{
"binMin": 0,
"binMax": 360,
"binName": "0 to +6 minutes",
"count": 51,
"percentOfTotal": 0.25
},
{
"binMin": 360,
"binMax": 600,
"binName": "+6 to +10 minutes",
"count": 32,
"percentOfTotal": 0.1568627450980392
},
{
"binMin": 600,
"binMax": 1200,
"binName": "+10 to +20 minutes",
"count": 90,
"percentOfTotal": 0.4411764705882353
}
]
},
"layer": [
{"mark": "bar"},
{
"transform": [
{
"calculate": "format(datum.percentOfTotal, '.0%')",
"as": "perc_total"
},
{
"calculate": "datum.count+' (' +datum.perc_total +')'",
"as": "countPercentText"
}
],
"mark": {
"type": "text",
"align": "center",
"yOffset": -10,
"fontSize": 18
},
"encoding": {"text": {"field": "countPercentText", "type": "ordinal"}}
}
],
"encoding": {
"x": {
"field": "binName",
"type": "nominal",
"title": "Wait Time for Transfer (Minutes)",
"axis": {"titleFontSize": 18, "labelAngle": 0, "labelFontSize": 14},
"sort": {"field": "binMin"}
},
"y": {
"field": "count",
"type": "quantitative",
"title": "Total Transfers",
"axis": {"titleFontSize": 18, "labelFontSize": 14}
},
"color": {
"legend": false,
"field": "binMin",
"scale": {"range": ["red", "orange", "green", "yellowgreen"]}
}
}
}

Customise vega-lite Line Graph Axis Labels

What I want to do is really simple, but I just can't seem to get it right. I have a feeling I'm going to be embarrassed by the answer!
I have a line graph with "attempt" along the x-axis and "grade" along the y-axis, with grade being a number between 0 and 100. I simply want to change the y-axis so that, instead of seeing the raw number, a grade is show, say with 0 - 20 representing "E", 20-40 being "D" etc up to "A" (80-100). How can I do that? I don't want to use discrete values because I want to visually show where within a grade boundary a grade falls. I'm not sure whether I yet want to simply display the grade bands on the line or put them in the middle of their ticks, but just getting somewhere with this would help a lot!
Here is what I've been working with in the vega-lite editor:
{
"$schema": "https://vega.github.io/schema/vega-lite/v3.json",
"data": {
"values": [
{
"attempt": 1,
"score": 30
},
{
"attempt": 2,
"score": 60
},
{
"attempt": 3,
"score": 75
},
{
"attempt": 4,
"score": 58
},
{
"attempt": 5,
"score": 67
}
]
},
"mark": {
"type": "line",
"color": "#22bc9a",
"point": {
"filled": false
}
},
"encoding": {
"x": {
"field": "attempt",
"type": "quantitative",
"axis": {
"grid": false,
"tickCount": 5,
"title": "Attempt"
}
},
"y": {
"field": "score",
"scale": {"domain": [0, 100]},
"type": "quantitative",
"axis": {
"tickCount": 5,
"title": "Grade"
}
},
"opacity": {"value": 0.3}
},
"config": {
"autosize": "fit",
"axis": {
"labelColor": "#bebec8",
"tickColor": "#bebec8",
"titleColor": "black",
"titleFontWeight": "normal",
"titleFontSize": 11,
"labelFont": "Helvetica",
"titleFont": "Helvetica",
"gridOpacity": 0.4,
"gridWidth": 1.5,
"domain": false
},
"view": {
"strokeWidth": 0
}
}
}
Thanks in advance.
What about something like this: I add a dataframe with the grade category, and use this to layer some text. I remove the labels of the axis and so the text acts as if it were the labels of the axis.
The chart looks like this, and here is a link to the editor:
Schema (Note that I did it with Python's Altair, so it may not be canonical Vega-lite, and I did not use your settings either, sorry about that):
{
"$schema": "https://vega.github.io/schema/vega-lite/v2.6.0.json",
"config": {
"view": {
"height": 300,
"width": 400
}
},
"datasets": {
"data-1acee7c5d817865a529b53e022474ce8": [
{
"label": "E",
"x_min": 1,
"y_med": 10
},
{
"label": "D",
"x_min": 1,
"y_med": 30
},
{
"label": "C",
"x_min": 1,
"y_med": 50
},
{
"label": "B",
"x_min": 1,
"y_med": 70
},
{
"label": "A",
"x_min": 1,
"y_med": 90
}
],
"data-8e6359ea3034b8410708361bb10fafd5": [
{
"attempt": 1,
"score": 30
},
{
"attempt": 2,
"score": 60
},
{
"attempt": 3,
"score": 75
},
{
"attempt": 4,
"score": 58
},
{
"attempt": 5,
"score": 67
}
]
},
"layer": [
{
"data": {
"name": "data-1acee7c5d817865a529b53e022474ce8"
},
"encoding": {
"text": {
"field": "label",
"type": "ordinal"
},
"x": {
"field": "x_min",
"scale": {
"zero": false
},
"type": "quantitative"
},
"y": {
"axis": {
"labels": false,
"tickCount": 5,
"ticks": false
},
"field": "y_med",
"type": "quantitative"
}
},
"mark": {
"dx": -15,
"dy": 8,
"size": 15,
"type": "text"
}
},
{
"data": {
"name": "data-8e6359ea3034b8410708361bb10fafd5"
},
"encoding": {
"x": {
"axis": {
"tickCount": 5
},
"field": "attempt",
"title": "Attempt",
"type": "quantitative"
},
"y": {
"field": "score",
"scale": {
"domain": [
0,
100
]
},
"title": "Grade",
"type": "quantitative"
}
},
"mark": {
"point": true,
"type": "line"
}
}
]
}
Using a slighly modified dataframe for the categories (with x_max, y_min and y_max added), you can add another layer with colored rectangles, that can help read the values:
Here is a link to the editor
And here is the schema
{
"$schema": "https://vega.github.io/schema/vega-lite/v2.6.0.json",
"config": {
"view": {
"height": 300,
"width": 400
}
},
"datasets": {
"data-1acee7c5d817865a529b53e022474ce8": [
{
"label": "E",
"x_min": 1,
"y_med": 10
},
{
"label": "D",
"x_min": 1,
"y_med": 30
},
{
"label": "C",
"x_min": 1,
"y_med": 50
},
{
"label": "B",
"x_min": 1,
"y_med": 70
},
{
"label": "A",
"x_min": 1,
"y_med": 90
}
],
"data-39ffbda2b5d5fe96de84d9e308d920ff": [
{
"label": "E",
"x_max": 5,
"x_min": 1,
"y_max": 20,
"y_med": 10,
"y_min": 0
},
{
"label": "D",
"x_max": 5,
"x_min": 1,
"y_max": 40,
"y_med": 30,
"y_min": 20
},
{
"label": "C",
"x_max": 5,
"x_min": 1,
"y_max": 60,
"y_med": 50,
"y_min": 40
},
{
"label": "B",
"x_max": 5,
"x_min": 1,
"y_max": 80,
"y_med": 70,
"y_min": 60
},
{
"label": "A",
"x_max": 5,
"x_min": 1,
"y_max": 100,
"y_med": 90,
"y_min": 80
}
],
"data-8e6359ea3034b8410708361bb10fafd5": [
{
"attempt": 1,
"score": 30
},
{
"attempt": 2,
"score": 60
},
{
"attempt": 3,
"score": 75
},
{
"attempt": 4,
"score": 58
},
{
"attempt": 5,
"score": 67
}
]
},
"layer": [
{
"data": {
"name": "data-39ffbda2b5d5fe96de84d9e308d920ff"
},
"encoding": {
"color": {
"field": "label",
"scale": {
"scheme": "greenblue"
},
"type": "ordinal"
},
"x": {
"field": "x_min",
"scale": {
"zero": false
},
"title": "Attempt",
"type": "quantitative"
},
"x2": {
"field": "x_max",
"type": "quantitative"
},
"y": {
"axis": null,
"field": "y_min",
"type": "quantitative"
},
"y2": {
"field": "y_max",
"type": "quantitative"
}
},
"mark": "rect"
},
{
"data": {
"name": "data-1acee7c5d817865a529b53e022474ce8"
},
"encoding": {
"text": {
"field": "label",
"type": "ordinal"
},
"x": {
"field": "x_min",
"scale": {
"zero": false
},
"type": "quantitative"
},
"y": {
"axis": {
"labels": false,
"tickCount": 5,
"ticks": false
},
"field": "y_med",
"type": "quantitative"
}
},
"mark": {
"dx": -15,
"dy": 8,
"size": 15,
"type": "text"
}
},
{
"data": {
"name": "data-8e6359ea3034b8410708361bb10fafd5"
},
"encoding": {
"x": {
"axis": {
"tickCount": 5
},
"field": "attempt",
"title": "Attempt",
"type": "quantitative"
},
"y": {
"field": "score",
"scale": {
"domain": [
0,
100
]
},
"title": "Grade",
"type": "quantitative"
}
},
"mark": {
"point": true,
"type": "line"
}
}
]
}
To get it working, I first had to change the encoding of the x and y axis to be ordinal. Then I mapped your input data values to letter grades before creating the schema:
//replace every score value with correct letter grade
values.forEach(datapoint => {
if(datapoint.score > 90) {
datapoint.score = "A";
} else if(datapoint.score > 80) {
datapoint.score = "B";
} else if (datapoint.score > 70) {
//so on...
}
});
Here is a working example in the vega-lite editor:
Line Chart with Ordinal Values
Here is the schema:
{
"$schema": "https://vega.github.io/schema/vega-lite/v3.json",
"data": {
"values": [
{
"attempt": 1,
"score": "F"
},
{
"attempt": 2,
"score": "D"
},
{
"attempt": 3,
"score": "C"
},
{
"attempt": 4,
"score": "F"
},
{
"attempt": 5,
"score": "D"
}
]
},
"mark": {
"type": "line",
"color": "#22bc9a",
"point": {
"filled": false
}
},
"encoding": {
"x": {
"field": "attempt",
"type": "ordinal",
"axis": {
"title": "Attempt"
}
},
"y": {
"field": "score",
"type": "ordinal",
"axis": {
"title": "Grade"
}
},
"opacity": {"value": 0.3}
},
"config": {
"autosize": "fit",
"axis": {
"labelColor": "#bebec8",
"tickColor": "#bebec8",
"titleColor": "black",
"titleFontWeight": "normal",
"titleFontSize": 11,
"labelFont": "Helvetica",
"titleFont": "Helvetica",
"gridOpacity": 0.4,
"gridWidth": 1.5
},
"view": {
"strokeWidth": 0
}
}
}

source code of the HTML5 version of Angry birds visible?

Today I read that Rovio released Angry birds for Chrome i.e HTML5 compatible. Does it mean that anyone can see the source code of the HTML5 version of the game now?
Yes, but it's not very readable as it looks to be generated from a Java input, probably via GWT.
The levels are stored as JSON – here's the level 2 file:
{
"camera": [
{
"bottom": -51.69,
"id": "Slingshot",
"left": -28.397,
"right": 47.013,
"top": -51.69,
"x": 28.621,
"y": -13.985
},
{
"bottom": -49.048,
"id": "Castle",
"left": 12.344,
"right": 82.582,
"top": -49.048,
"x": 66.776,
"y": -13.929
}
],
"counts": {
"birds": 5,
"blocks": 27
},
"id": "pack1/Level57.lua",
"scoreEagle": 29000,
"scoreGold": 60000,
"scoreSilver": 47000,
"theme": "BACKGROUND_BLUE_GRASS",
"world": {
"bird_1": {
"angle": 0,
"id": "BIRD_RED",
"x": 9.93,
"y": -4.328
},
"bird_2": {
"angle": 0,
"id": "BIRD_RED",
"x": 7.489,
"y": -4.339
},
"bird_3": {
"angle": 0,
"id": "BIRD_RED",
"x": 5.417,
"y": -4.249
},
"bird_4": {
"angle": 0,
"id": "BIRD_RED",
"x": 3.42,
"y": -4.339
},
"bird_5": {
"angle": 0,
"id": "BIRD_RED",
"x": 0,
"y": -1.035
},
"block_1": {
"angle": 90.027,
"id": "STONE_BLOCK_4X1",
"x": 61.498,
"y": -7.314
},
"block_10": {
"angle": 180,
"id": "TERRAIN_TEXTURED_HILLS_10X10",
"x": 74.143,
"y": -0.157
},
"block_11": {
"angle": 180,
"id": "TERRAIN_TEXTURED_HILLS_10X10",
"x": 83.499,
"y": -0.107
},
"block_12": {
"angle": 180,
"id": "TERRAIN_TEXTURED_HILLS_10X10",
"x": 92.925,
"y": -0.058
},
"block_13": {
"angle": 0,
"id": "TERRAIN_TEXTURED_HILLS_10X10",
"x": 7.156,
"y": 1.484
},
"block_14": {
"angle": 135,
"id": "TERRAIN_TEXTURED_HILLS_10X10",
"x": 111.252,
"y": -18.195
},
"block_15": {
"angle": 24.537,
"id": "TERRAIN_TEXTURED_HILLS_5X5",
"x": 3.616,
"y": -0.22
},
"block_16": {
"angle": 45,
"id": "TERRAIN_TEXTURED_HILLS_5X5",
"x": 11.912,
"y": -0.041
},
"block_17": {
"angle": 45,
"id": "TERRAIN_TEXTURED_HILLS_5X5",
"x": 103.199,
"y": -0.037
},
"block_18": {
"angle": 45,
"id": "TERRAIN_TEXTURED_HILLS_5X5",
"x": 106.374,
"y": -3.514
},
"block_19": {
"angle": 45,
"id": "TERRAIN_TEXTURED_HILLS_5X5",
"x": 109.7,
"y": -0.188
},
"block_2": {
"angle": 89.999,
"id": "STONE_BLOCK_4X1",
"x": 68.165,
"y": -7.317
},
"block_20": {
"angle": 359.713,
"id": "PIG_BASIC_SMALL",
"x": 85.85,
"y": -15.34
},
"block_21": {
"angle": 2.262,
"id": "PIG_BASIC_SMALL",
"x": 75.084,
"y": -11.419
},
"block_22": {
"angle": 7.16,
"id": "PIG_BASIC_SMALL",
"x": 68.143,
"y": -11.427
},
"block_23": {
"angle": 0.153,
"id": "PIG_BASIC_SMALL",
"x": 61.475,
"y": -11.411
},
"block_24": {
"angle": 179.984,
"id": "WOOD_BLOCK_4X1",
"x": 85.911,
"y": -13.755
},
"block_25": {
"angle": 180.065,
"id": "WOOD_BLOCK_4X1",
"x": 75.051,
"y": -9.834
},
"block_26": {
"angle": 180.172,
"id": "WOOD_BLOCK_4X1",
"x": 68.284,
"y": -9.842
},
"block_27": {
"angle": 0.112,
"id": "WOOD_BLOCK_4X1",
"x": 61.463,
"y": -9.83
},
"block_3": {
"angle": 90.017,
"id": "STONE_BLOCK_4X1",
"x": 74.983,
"y": -7.316
},
"block_4": {
"angle": 270,
"id": "STONE_BLOCK_8X1",
"x": 85.892,
"y": -9.272
},
"block_5": {
"angle": 359.999,
"id": "TERRAIN_TEXTURED_HILLS_32X2",
"x": 75.877,
"y": -4.433
},
"block_6": {
"angle": 225.001,
"id": "TERRAIN_TEXTURED_HILLS_10X10",
"x": 98.165,
"y": -5.15
},
"block_7": {
"angle": 225.001,
"id": "TERRAIN_TEXTURED_HILLS_10X10",
"x": 104.615,
"y": -11.599
},
"block_8": {
"angle": 90,
"id": "TERRAIN_TEXTURED_HILLS_10X10",
"x": 64.836,
"y": -0.135
},
"block_9": {
"angle": 135,
"id": "TERRAIN_TEXTURED_HILLS_10X10",
"x": 60.034,
"y": 1.675
}
}
}
This means that you could potentially inject your own levels by intercepting the requests.
Yes, but it is heavily obfuscated, see small part of code below:
function d3(){this.b=new Date}
function MK(){this.o=EU.d.Nc()}
function TE(b,c){b.i.k=c;b.f=4}
function PU(b,c,d){b.f=c;b.g=d}
function YF(b){b.f=6;b.b=false}
function YH(b){b.f=6;b.b=false}
function iG(b){b.f=7;b.b=false}
They use the google PlayN framework for the HTML5 port.
See https://developers.google.com/playn/
It means you can see the HTML5 code, but it will not be very useful since it's compiled with GWT and not very understandable.