Using the Blazor.ChartJs library, I have a series of horizontal stacked bar charts, the purpose of which is to show the percentage complete. The red portion is the yearly goal and the blue is actual number achieved so far:
The first chart could have a goal of $10,000,000, the second $3,000,000, the third $7,000,000, etc. But I would like each chart to expand to the end of the table cell in which it is contained because I don't care about the numbers, just the percentages:
As you can tell in the first chart, the numbers are stopping before the end of the cell. I am adding the charts to HTML table cells, one for each statistic I'm tracking:
<table>
<tbody>
<tr>
<td></td>
<td>% Complete</td>
</tr>
#for (int i = 0; i < typeOfProject.Count; i++)
{
<tr>
<td><Chart Config="_configs[i]" Height="8" /></td>
<td>#String.Format("{0:P0}", percentagesComplete[i])</td>
</tr>
}
</tbody>
</table>
I build the _configs collection using a method that returns a BarConfig options, and the configurations are identical for all charts. I have experimented with the Responsive and MaintainAspectRatio properties, but this does not seem to affect the results.
config = new BarConfig
{
Options = new BarOptions
{
MaintainAspectRatio = true,
Responsive = true,
Legend = new Legend
{
Display = false
},
Scales = new BarScales
{
XAxes = new List<CartesianAxis>
{
new BarLinearCartesianAxis
{
Stacked = true,
OffsetGridLines = false,
Offset = false,
Display = AxisDisplay.False
}
},
YAxes = new List<CartesianAxis>
{
new BarCategoryAxis
{
Stacked = true,
OffsetGridLines = false,
Offset = false,
Display = AxisDisplay.False,
BarThickness = 45
}
}
}
}
I have also add a div tag around the Canvas object in each cell as mentioned in this question, but that wasn't the solution to my problem.
How can I expand the bars to the end of the canvas in each cell?
I created my own code to do this because I could not get the chart width to consistently expand to the end of the table cell. The requirements changed slightly as well, so I needed to draw an empty rectangle representing 100% of the goal and then filling the rectangle with a percentage of how complete the goal was.
First, I have a JavaScript method that gets the width of the canvas, calculates how many pixels to draw based on that width and the "value" variable (which is a percentage), then uses the context to first draw a clear rectangle. It draws a filled rectangle representing the progress to the goal:
function buildCanvas(canvasId, value) {
var canvasWidth = document.getElementById(canvasId).offsetWidth;
var calculatedWidth = parseInt(canvasWidth * value);
var canvasToDraw = document.querySelector('#' + canvasId);
const ctx1 = canvasToDraw.getContext('2d');
ctx1.clearRect(0, 0, 300, 150);
ctx1.fillStyle = 'rgba(173, 116, 76, 1.0)';
ctx1.fillRect(0, 0, calculatedWidth, 150);
}
I define the chart that I want to draw:
<canvas id="chart" style="border: 1px solid #AD744C; height: 18px; width: 300px">
I then inject IJSRuntime and call the JavaScript method to draw the rectangle, the value being the goal divided by the value to get a percentage:
#inject IJSRuntime JsRuntime
...
JsRuntime.InvokeAsync<string>("buildCanvas", $"chart", $"{value}");
I got an attributed string with an image in a textview, the attributed image is displaying fine. The issue is whenever I add the image it replaces the text inside the textview. I want to add the attributed image and then place the cursor at the end of the image, so that i can add text below the image. This is what I tried so far, thank in advance.
let attachment = NSTextAttachment()
attachment.image = selectedImage
let newImageWidth = (textView.bounds.size.width - 10 )
let scale = newImageWidth/(selectedImage.size.width)
let newImageHeight = selectedImage.size.height * scale
attachment.bounds = CGRect(x: 0, y: 0, width: newImageWidth, height: newImageHeight)
let myAttributedString = NSAttributedString(attachment: attachment)
textView.textStorage.insert(myAttributedString, at: textView.selectedRange.location)
I fixed my issue my creating an NSMutableAttributedString() and add a new line attribute to put cursor to the bottom of the image. Hope this help someone.
var mutableAttrString = NSMutableAttributedString()
let attachment = NSTextAttachment()
attachment.image = selectedImage
let newImageWidth = (textView.bounds.size.width - 10 )
let scale = newImageWidth/(selectedImage.size.width)
let newImageHeight = selectedImage.size.height * scale
attachment.bounds = CGRect(x: 0, y: 0, width: newImageWidth, height: newImageHeight)
let myAttributedString = NSAttributedString(attachment: attachment)
let text:[String:Any] = [NSFontAttributeName:UIFont.boldSystemFont(ofSize: 20)]
// Fixed my problem of having image replacing text, instead I place the text at the bottom of the image
let textAttr = NSMutableAttributedString(string: "bottom of image \n", attributes: text)
mutableAttrString.append(myAttributedString)
mutableAttrString.append(textAttr)
textView.attributedText = mutableAttrString
Im trying to filter column 'time' in visualization data table using getFilteredRows(filters) method.I provided column value with minimum and maximum values as,
var timesheet_dataTable = new google.visualization.DataTable(data, 0.6);
var time_filter = timesheet_dataTable.getFilteredRows([{column: 3, minValue: '2:28 PM', maxValue: '3:01 PM'}]);
and then created data view with setRows method to display the data but the table displayed without filtering the data.I checked with other column values and received proper output.So whether 'timeofday' data type is supported in this type of filters?
Is there any other method to filter column based on time?
Update:
This is the code for formatting and passing value to the visualization table.Value of variable startTime will be like '14:28:12'.
val datetimeStart: String = "Date(0,0,0,"
val datetimeEnd: String = ")"
val simpleDateTimeFormat = new SimpleDateFormat("HH,mm,ss")
Json.obj("v" -> JsString(datetimeStart + (simpleDateTimeFormat.format(tsl.startTime)).toString() + datetimeEnd))
before displaying in visualization table i have used formatter as:
var formatter_short1 = new google.visualization.DateFormat({pattern:'h:mm aa'});
formatter_short1.format(timesheet_dataTable,3);
The "timeofday" data type is supported by the filter method, you just need to use it correctly:
// filter column 3 from 2:28PM to 3:01PM
var time_filter = timesheet_dataTable.getFilteredRows([{
column: 3,
minValue: [14, 28, 0, 0],
maxValue: [15, 1, 0, 0]
}]);
var view = new google.visualization.DataView(timesheet_dataTable);
view.setRows(time_filter);
Make sure you are using the view you create to draw your chart, instead of the DataTable:
chart.draw(view, options);
[edit - example for filtering "datetime" type column]
// filter column 3 from 2:28PM to 3:01PM
var time_filter = timesheet_dataTable.getFilteredRows([{
column: 3,
minValue: new Date(0, 0, 0, 14, 28, 0, 0),
maxValue: new Date(0, 0, 0, 15, 1, 0, 0)
}]);
var view = new google.visualization.DataView(timesheet_dataTable);
view.setRows(time_filter);
I have my chart working ok, however I would like to use a custom step for my vertical y-axis. At the moment it seems to be automatic and is spaced out as below:
1,500,000
3,000,000
4,500,000
I would prefer it to be:
100,000
200,000
300,000
and so on...
Is there any way I can set this, I have looked through all the documentation but can't figure it out.
Here is my code:
var chart = new google.visualization.LineChart(document.getElementById('chart'));
chart.draw(chartData, { width: 1600, height: 900, title: 'Company Performance',
yAxis: { gridlineColor: '#ff0000' },
xAxis: { gridlineColor: '#ff0000' }
}
);
My data is company profit for each week of the year, y-axis is profit, x-axis is the week number.
Hope somebody can help.
Paul
this is how I do it:
var options = {
vAxis: { // same thing for horisontal, just use hAxis
viewWindow: { // what range will be visible
max: 120,
min: 0
},
gridlines: {
count: 12 // how many gridlines. You can set not the step, but total count of gridlines.
}
}
};
all the best ;)
For as far as I know this cannot be done automatically with Google Charts settings.
I've written a javascript function to do this.
To use it you can create a nice sequence that can be used as ticks for the vertical axis:
var prettyTicks = getChartTicks(0, chartData.getColumnRange(1).max);
The line for the xAxis should be changed to apply the ticks:
yAxis: { gridlineColor: '#ff0000', ticks: prettyTicks },
Here is the javascript method to create the ticks. It will create a tick for each value of 10 and if that creates too many ticks then it will do this for each 100 or 1000 etc.
// Creates an array of values that can be used for the tick property of the Google Charts vAxis
// The values provide a nice scale to have a clean view.
var getChartTicks = function (min, max) {
// settings
var maxTicks = 8;
var tickSize = 10;
// determine the range of the values and the number of ticks
var newMin;
var newMax;
var nrOfTicks;
var appliedTickSize = 1;
while (newMin == null || nrOfTicks > maxTicks) {
appliedTickSize *= tickSize;
newMin = Math.floor(min / appliedTickSize) * appliedTickSize;
newMax = Math.ceil(max / appliedTickSize) * appliedTickSize;
nrOfTicks = (newMax - newMin) / appliedTickSize;
}
// generate the tick values which will be applied to the axis
var ticks = new Array();
var i = 0;
for (var v = newMin; v <= newMax; v += appliedTickSize) {
ticks[i++] = v;
}
return ticks;
}
So to summarize, after adding this method your code could then be changed to:
var prettyTicks = getChartTicks(0, chartData.getColumnRange(1).max);
var chart = new google.visualization.LineChart(document.getElementById('chart'));
chart.draw(chartData, { width: 1600, height: 900, title: 'Company Performance',
yAxis: { gridlineColor: '#ff0000', ticks: prettyTicks },
xAxis: { gridlineColor: '#ff0000' }
}
);
Hi Please refer google chart api.There are several parameters available according to your requirement like
chbh = Bar width and spacing ...
chco = Series colors ...
chd = Chart data string...
chdl,chdlp, chdls=Chart legend text and style...
chds Scale for text format with custom range...
chem = Dynamic icon markers...
for more information
http://code.google.com/apis/chart/image/docs/chart_params.html
I was wondering how I can make a simple bar chart that perhaps has day as the x-axis, with values 'today' and 'yesterday', and the y-axis as perhaps 'time' with corresponding values '1' and '2'. I guess I'm confused as to how to set text as the values for the x-axis, how to show the y axis, and what exactly r.g.axis does...
(I found an example using axis = r.g.axis(0,300,400,0,500,8,2) and I only know it's the xpos, ypos,width, ??, ?? num ticks, ??). Any insight would be great! Or a page with more fully featured bar chart examples (labels, etc). Thanks.
For the sake of all those googling this:
r.g.axis(x_start, y_start, x_width, from, to, steps, orientation, labels, type, dashsize)
x_start and y_start: distance of the axis text from the bottom left corner
x_width: position of the end of the text along the x axis
from and to: used to specify and range to use instead of using the labels argument
steps: is the number of ticks - 1
orientation: seems to specify x-axis vs. y-axis
type: is the type of tick mark used.
This was all deduced from the source code. I think I'll be switching to a charting library with documentation now...
The current code (Raphaeljs 2.0) has changed and has to be slightly adapted to use Raphael.g.axis instead of r.g.axis:
Raphael.g.axis(85,230,310,null,null,4,2,["Today", "Yesterday",
"Tomorrow", "Future"], "|", 0, r)
You're on the right track. You use g.axis and the positional arguments for setting the text is found in the 'text' arg (positional) and for toggling the y using the 'orientation' args. I added an example here,
Barchart with text x-axis
Reading this Q&A and a dozen like it, I still could not get gRaphaƫl to show proper labels for a bar chart. The recipes all seemed to refer to older versions of the library, or to github pages that are no longer there. gRaphaƫl produces some great looking output--but its docs leave much to be desired.
I was, however, able to use a combination of Firebug and Inspect This Element to follow the code and see what it produced. Diving into the barchart object, the required geometry is right there. To save others the frustration, here's how I solved the problem:
<script>
function labelBarChart(r, bc, labels, attrs) {
// Label a bar chart bc that is part of a Raphael object r
// Labels is an array of strings. Attrs is a dictionary
// that provides attributes such as fill (text color)
// and font (text font, font-size, font-weight, etc) for the
// label text.
for (var i = 0; i<bc.bars.length; i++) {
var bar = bc.bars[i];
var gutter_y = bar.w * 0.4;
var label_x = bar.x
var label_y = bar.y + bar.h + gutter_y;
var label_text = labels[i];
var label_attr = { fill: "#2f69bf", font: "16px sans-serif" };
r.text(label_x, label_y, label_text).attr(label_attr);
}
}
// what follows is just setting up a bar chart and calling for labels
// to be applied
window.onload = function () {
var r = Raphael("holder"),
data3 = [25, 20, 13, 32, 15, 5, 6, 10],
txtattr = { font: "24px 'Allerta Stencil', sans-serif", fill: "rgb(105, 136, 39)"};
r.text(250, 10, "A Gratuitous Chart").attr(txtattr);
var bc = r.barchart(10, 10, 500, 400, data3, {
stacked: false,
type: "soft"});
bc.attr({fill: "#2f69bf"});
var x = 1;
labelBarChart(r, bc,
['abc','b','card','d','elph','fun','gurr','ha'],
{ fill: "#2f69bf", font: "16px sans-serif" }
);
};
</script>
<div id="holder"></div>
There are a bunch of little cleanups you could do to labelBarChart(), but this basically gets the job done.
Here's a function I wrote for adding the labels. It's not particularly elegant but it will add the labels:
Raphael.fn.labelBarChart = function(x_start, y_start, width, labels, textAttr) {
var paper = this;
// offset width and x_start for bar chart gutters
x_start += 10;
width -= 20;
var labelWidth = width / labels.length;
// offset x_start to center under each column
x_start += labelWidth / 2;
for ( var i = 0, len = labels.length; i < len; i++ ) {
paper.text( x_start + ( i * labelWidth ), y_start, labels[i] ).attr( textAttr );
}
};
Usage is as follows:
var paper = Raphael(0, 0, 600, 400);
var chart = paper.barchart(0, 0, 600, 380, [[63, 86, 26, 15, 36, 62, 18, 78]]);
var labels = ['Col 1', 'Col 2', 'Col 3', 'Col 4', 'Col 5', 'Col 6', 'Col 7', 'Col 8'];
paper.labelBarChart(0, 390, 600, labels, {'font-size': 14});
I would like to propose a solution of an issue of the labelBarChart function proposed by Jonathan Eunice.
considering stacked bar-graphes (or other bar-graphes with more than one array of values), I added a test on bc.bars[0] in case the bc.bars.length means the number of arrays of values stacked.
This lead to the code :
<script>
function labelBarChart(r, bc, labels, attrs) {
// Label a bar chart bc that is part of a Raphael object r
// Labels is an array of strings. Attrs is a dictionary
// that provides attributes such as fill (text color)
// and font (text font, font-size, font-weight, etc) for the
// label text.
//Added test : replace bc.bars by generic variable barsRef
var barsRef = (typeof bc.bars[0].length === 'undefined') ? bc.bars : bc.bars[0];
var bar, gutter_y, label_x, label_y, label_text;
//Added consideration of set attrs (if set)
var label_attr = (typeof attrs === 'undefined') ? {} : attrs;
label_attr['fill'] = (typeof label_attr['fill'] === 'undefined') ? "#2f69bf" : label_attr['fill'];
label_attr['font'] = (typeof label_attr['font'] === 'undefined') ? "16px sans-serif" : label_attr['font'];
for (var i = 0; i<barsRef.length; i++) {
bar = barsRef[i];
gutter_y = bar.w * 0.4;
label_x = bar.x
label_y = bar.y + bar.h + gutter_y;
label_text = labels[i];
r.text(label_x, label_y, label_text).attr(label_attr);
}
}
// what follows is just setting up a bar chart and calling for labels
// to be applied
// I added an array of data to illustrate : data4
window.onload = function () {
var r = Raphael("holder"),
data3 = [25, 20, 13, 32, 15, 5, 6, 10],
data4 = [0, 2, 1, 40, 1, 65, 46, 11],
txtattr = { font: "24px 'Allerta Stencil', sans-serif", fill: "rgb(105, 136, 39)"};
r.text(250, 10, "A Gratuitous Chart").attr(txtattr);
var bc = r.barchart(10, 10, 500, 400, [data3, data4] {
stacked: true,
type: "soft"});
bc.attr({fill: "#2f69bf"});
labelBarChart(r, bc,
['abc','b','card','d','elph','fun','gurr','ha'],
{ fill: "#2f69bf", font: "16px sans-serif" }
);
};
</script>
<div id="holder"></div>
I just tested it with 2 arrays of values stacked.