I'm developping a WinForms app in C# for plotting multiple lines on a logarithmic scale. I'm trying to display my data on a grid that would look like this:
Logarithmic grid, with major and minor ticks at specified positions.
The data I'm plotting matches specific values on the X-Axis (1, 2, 4, 8 and 16) but there's an offset on the axis (the origin is lower than 1 and the max value is greater than 16) because I have to plot several lines with error bars (I need some extra width).
I'm struggling to find a way to specify exact positions for where my major/minor ticks to be placed.
The closer I got was by specifying chart.ChartAreas[0].AxisX.LogarithBase = 2 and chart.ChartAreas[0].AxisY.LogarithBase = 2, but since my origin is not set at (1,1) I get this as a result:
The closer I got to the grid I'm looking for.
Ideally, I'd like to have a collection of values that specifies the position of the ticks. How one could achieve that? I feel like I'm getting closer by using the CustomLabel class but I'm not in there yet.
Thanks!
I tried to add CustomLabels for specifying the ticks position but couldn't find a way to have the ticks placed at the position I specified: I only found a way to put ticks at specified "indexes" that I struggle to have placed at the correct positions.
I tried playing with the axis intervals too, with no success either.
Maybe not the answer you are looking for so don't worry about accepting, but for you or anyone who happens to read this. Using a 3rd party tool will make this easy, plus you can ask questions directly to the authors of the chart. Full disclosure, I own Gigasoft.
Instead of trying to force your chart to produce the grid in your current approach, instead, maybe your charting api has a feature to draw random annotations, lines, and text, and construct your own axes/grid via these lines and text.
For example, with Gigasoft ProEssentials, the x axis could be created as follows, the y axis would be similar. This is based on our example 110 in our demo.
Pesgo1.PeGrid.Option.LogScaleExpLabels = false; // optional
Pesgo1.PeAnnotation.Line.TextSize = 100; // to match size of normal grid text
Pesgo1.PeGrid.Option.ShowXAxis = ShowAxis.NoNumbersOrLabels; // hide default grid line labels
Pesgo1.PeAnnotation.Line.BottomMargin = "X"; // used with below to add space of line annotation text outside grid
Pesgo1.PeAnnotation.Line.ShowMargins = ShowMargins.Always; // adds space for line annotation text
int n = 0;
// add grid lines with grid number labels
Pesgo1.PeAnnotation.Line.XAxis[n] = 1.0;
Pesgo1.PeAnnotation.Line.XAxisType[n] = LineAnnotationType.GridLine;
Pesgo1.PeAnnotation.Line.XAxisText[n] = "|h1";
Pesgo1.PeAnnotation.Line.XAxisColor[n] = Color.FromArgb(200, 200, 200, 200);
n++;
Pesgo1.PeAnnotation.Line.XAxis[n] = 2.0;
Pesgo1.PeAnnotation.Line.XAxisType[n] = LineAnnotationType.GridLine;
Pesgo1.PeAnnotation.Line.XAxisText[n] ="|h2";
Pesgo1.PeAnnotation.Line.XAxisColor[n] = Color.FromArgb(200, 200, 200, 200);
n++;
Pesgo1.PeAnnotation.Line.XAxis[n] = 4.0;
Pesgo1.PeAnnotation.Line.XAxisType[n] = LineAnnotationType.GridLine;
Pesgo1.PeAnnotation.Line.XAxisText[n] = "|h4";
Pesgo1.PeAnnotation.Line.XAxisColor[n] = Color.FromArgb(200, 200, 200, 200);
n++;
Pesgo1.PeAnnotation.Line.XAxis[n] = 8.0;
Pesgo1.PeAnnotation.Line.XAxisType[n] = LineAnnotationType.GridLine;
Pesgo1.PeAnnotation.Line.XAxisText[n] = "|h8";
Pesgo1.PeAnnotation.Line.XAxisColor[n] = Color.FromArgb(200, 200, 200, 200);
n++;
Pesgo1.PeAnnotation.Line.XAxis[n] = 16.0;
Pesgo1.PeAnnotation.Line.XAxisType[n] = LineAnnotationType.GridLine;
Pesgo1.PeAnnotation.Line.XAxisText[n] = "|h16";
Pesgo1.PeAnnotation.Line.XAxisColor[n] = Color.FromArgb(200, 200, 200, 200);
n++;
// add extra grid lines 11 - 15
for (int x = 11; x <= 15; x++)
{
n++;
Pesgo1.PeAnnotation.Line.XAxis[n] = x;
Pesgo1.PeAnnotation.Line.XAxisType[n] = LineAnnotationType.Dot;
Pesgo1.PeAnnotation.Line.XAxisColor[n] = Color.FromArgb(80, 200, 200, 200);
Pesgo1.PeAnnotation.Line.XAxisText[n] = " ";
}
// add extra ticks 1 to 16
n++;
Pesgo1.PeAnnotation.Line.XAxis[n] = 3.0;
Pesgo1.PeAnnotation.Line.XAxisType[n] = LineAnnotationType.GridTick;
Pesgo1.PeAnnotation.Line.XAxisColor[n] = Color.FromArgb(200, 200, 200, 200);
Pesgo1.PeAnnotation.Line.XAxisText[n] = " ";
for (int x = 5; x <= 7; x++)
{
n++;
Pesgo1.PeAnnotation.Line.XAxis[n] = x;
Pesgo1.PeAnnotation.Line.XAxisType[n] = LineAnnotationType.GridTick;
Pesgo1.PeAnnotation.Line.XAxisColor[n] = Color.FromArgb(200, 200, 200, 200);
Pesgo1.PeAnnotation.Line.XAxisText[n] = " ";
}
for (int x = 9; x <= 15; x++)
{
n++;
Pesgo1.PeAnnotation.Line.XAxis[n] = x;
Pesgo1.PeAnnotation.Line.XAxisType[n] = LineAnnotationType.GridTick;
Pesgo1.PeAnnotation.Line.XAxisColor[n] = Color.FromArgb(200, 200, 200, 200);
Pesgo1.PeAnnotation.Line.XAxisText[n] = " ";
}
// Set the x axis range
Pesgo1.PeGrid.Configure.ManualScaleControlX = ManualScaleControl.MinMax;
Pesgo1.PeGrid.Configure.ManualMinX = .9;
Pesgo1.PeGrid.Configure.ManualMaxX = 18;
Pesgo1.PeAnnotation.Show = true; // show the line annotations
And the resulting image is ...
Related
I'm generating a barcode depending on how many inputs that the user set in the numericUpDown control. The problem is when generating a lot of barcodes, the other barcodes cannot be seen in the printpreviewdialog because it I cannot apply a nextline or \n every 4-5 Images.
int x = 0, y = 10;
for (int i = 1; i <= int.Parse(txtCount.Text); i++)
{
idcount++;
connection.Close();
Zen.Barcode.Code128BarcodeDraw barcode = Zen.Barcode.BarcodeDrawFactory.Code128WithChecksum;
Random random = new Random();
string randomtext = "MLQ-";
int j;
for (j = 1; j <= 6; j++)
{
randomtext += random.Next(0, 9).ToString();
Image barcodeimg = barcode.Draw(randomtext, 50);
resultimage = new Bitmap(barcodeimg.Width, barcodeimg.Height + 20);
using (var graphics = Graphics.FromImage(resultimage))
using (var font = new Font("Arial", 11)) // Any font you want
using (var brush = new SolidBrush(Color.Black))
using (var format = new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Far}) // Also, horizontally centered text, as in your example of the expected output
{
graphics.Clear(Color.White);
graphics.DrawImage(barcodeimg, 0, 0);
graphics.DrawString(randomtext, font, brush, resultimage.Width / 2, resultimage.Height, format);
}
x += 25;
}
e.Graphics.DrawImage(resultimage, x, y);
}
There's no "new lines" in rasterized graphics. There's pixels. You've got the right idea, every n number of images, add a new line. But since you're working with pixels, let's say every 4 images you're going to need to add a vertical offset by modifying the y coordinate of all your graphics draw calls. This offset, combined with a row height in pixels could look something like this:
var rowHeight = 250; // pixels
var maxColumns = 4;
var verticalOffset = (i % maxColums) * rowHeight;
Then, when you can supply a y coordinate, starting at or near 0, add the vertical offset to it.
I draw some charts using DataVisualization.Charting.Chart. All of my charts have inverse Y range.
For Example "-20 to 20" or "-150 to 150". It means that Zero is always in the middle of Y range, there is no problem in drawing but the chart never makes a label for Zero.
For Example I have this labels -20,-15,-10,-5,5,10,15,20. I always want to see the Zero in Y axis labels. See the image:
I did it using CustomLabels :
int maxRange = 20;
int yInterval = 5;
var minRange = (maxRange * -1);
area.AxisY.Minimum = minRange;
area.AxisY.Maximum = maxRange;
area.AxisY.LabelStyle.Format = "#";
area.AxisY.Interval = yInterval;
int yVal = minRange;
while (yVal <= maxRange)
{
area.AxisY.CustomLabels.Add(yVal - 0.5, yVal + 0.5, yVal.ToString());
yVal += yInterval;
}
for this min & max must in 10s series
chart.ChartAreas[0].AxisX.Interval = 10;
chart.ChartAreas[0].AxisY.Interval = 10;
I've spent hours trying to solve this silly problem. I create an histogram with asp chart control. All I want to do is have the xaxis label on the left of the column instead of centered on it. Xaxis lable doesn't seem to have a position property like series do, so I can't figure it out and it's frustrating.
Here's a sample code of the type of graphic I'm talking about to show you what I get approximately:
private void Graphique()
{
// Creating the series
Series series2 = new Series("Series2");
// Setting the Chart Types
series2.ChartType = SeriesChartType.Column;
// Adding some points
series2.Points.AddXY(1492, 12);
series2.Points.AddXY(2984, 0);
series2.Points.AddXY(4476, 1);
series2.Points.AddXY(5968, 2);
series2.Points.AddXY(7460, 2);
series2.Points.AddXY(8952, 12);
series2.Points.AddXY(10444, 4);
series2.Points.AddXY(11936, 3);
series2.Points.AddXY(13428, 3);
series2.Points.AddXY(14920, 5);
series2.Points.AddXY(16412, 1);
Chart3.Series.Add(series2);
Chart3.Width = 600;
Chart3.Height = 600;
// Series visual
series2.YValueMembers = "Frequency";
series2.XValueMember = "RoundedValue";
series2.BorderWidth = 1;
series2.ShadowOffset = 0;
series2.IsXValueIndexed = true;
// Setting the X Axis
Chart3.ChartAreas["ChartArea1"].AxisX.IsMarginVisible = true;
Chart3.ChartAreas["ChartArea1"].AxisX.Interval = 1;
Chart3.ChartAreas["ChartArea1"].AxisX.Maximum = Double.NaN;
Chart3.ChartAreas["ChartArea1"].AxisX.Title = "kbps";
// Setting the Y Axis
Chart3.ChartAreas["ChartArea1"].AxisY.Interval = 2;
Chart3.ChartAreas["ChartArea1"].AxisY.Maximum = Double.NaN;
Chart3.ChartAreas["ChartArea1"].AxisY.Title = "Frequency";
}
Now my real chart looks like this, Actual result
I would like something similar to this website :
Desired layout chart
You see, the x label is on the left, which makes way more sense considering that each column of an histogram represents the frequency of a range of values.....
Any help would be appreciated...
Did you try to add CustomLabels to replace the default ones? For example:
for (int i = 0; i <= 10; i++) {
area.AxisX.CustomLabels.Add(i + 0.5, i + 1.5, i, 0, LabelMarkStyle.None);
}
The first two are for positioning and the third would be the text value of the label.
I don't understand something. If i don't use the customlabels, the chart will use the default label. And then if I move the scrollbar , the chart size won't adjust. The Chart view maintain the original size.
But if I use this code to change the label at row 0. (other rows don't have this problem)
chart1.ChartAreas[0].AxisY2.CustomLabels.Add((i) ,
(i+1), (ntemp * 10).ToString(), 0, LabelMarkStyle.SideMark);
And Move the scrollbar, the chart View will be a little different for size. The chart will flicker, and I don't want it.
Thanks in advance.
Here is example
Random rand = new Random();
chart1.Series.Clear();
var series = chart1.Series.Add("My Series");
series.ChartType = SeriesChartType.RangeBar;
series.Color = Color.Black;
series.YAxisType = AxisType.Secondary;
for (int i = 10; i > 2; i--)
series.Points.AddXY(i, (rand.Next(3600, 7200)), (rand.Next(30000, 80000)));
var chartArea = chart1.ChartAreas[series.ChartArea];
chartArea.BorderDashStyle = ChartDashStyle.Solid; //最外圍的框框
chartArea.BorderWidth = 10;
chartArea.AxisY.Enabled = AxisEnabled.False;
chartArea.AxisY2.Enabled = AxisEnabled.True;
chartArea.AxisY2.LabelStyle.IntervalType = DateTimeIntervalType.Number;
chartArea.AxisY2.Interval = 3600;
chartArea.AxisY2.Minimum = 0;
chartArea.AxisY2.Maximum = 86400;
chartArea.AxisY2.ScaleView.Zoom(0, 3600 * 4);
for (int i = 0; i <= 24 * 6; i++)
{
int ntemp = i % 6;
if (ntemp != 0)
{
/*Problem Here !!*/
//chart1.ChartAreas[0].AxisY2.CustomLabels.Add((i) * 600, (i + 1) * 600, (ntemp * 10).ToString(), 0, LabelMarkStyle.Box);
}
}
chartArea.CursorY.AutoScroll = true;
chartArea.AxisY2.ScaleView.Zoomable = true;
chartArea.AxisY2.ScrollBar.ButtonStyle = ScrollBarButtonStyles.SmallScroll;
chartArea.AxisY2.ScrollBar.IsPositionedInside = false;
}
Well, I was intrigued about how and if this can be achieved with OxyPlot, and I think it can ...
Here's the code I've used, and here's a screenshot:
var model = new PlotModel("IntervalBarSeries") { LegendPlacement = LegendPlacement.Outside };
var temp_serie = new IntervalBarSeries
{
Title = "IntervalBarSeries 1",
FillColor = OxyColors.Black
};
var categoryAxis = new CategoryAxis
{
Position = AxisPosition.Left,
IsZoomEnabled = false, // No zoom on this axis
IsPanEnabled = false, // Right mouse move won't affect this axis
MajorGridlineStyle = LineStyle.Solid
,StartPosition = 1, EndPosition = 0 // This will reverse the order
};
var valueAxis = new LinearAxis(AxisPosition.Top)
{
MinimumPadding = 0.1, MaximumPadding = 0.1,
IsZoomEnabled = true,
MajorGridlineStyle = LineStyle.Solid,
MajorStep = 3600,
AbsoluteMinimum = 0
};
for (int i = 10; i > 2; i--)
{
temp_serie.Items.Add(new IntervalBarItem {
Start = rand.Next(3600, 7200),
End = rand.Next(30000, 80000)
});
categoryAxis.Labels.Add("Activity "+i);
}
model.Series.Add(temp_serie);
model.Axes.Add(categoryAxis);
model.Axes.Add(valueAxis);
MyPlotModel = model;
Now, I'm using MVVM and just binding to the plot model from my View with:
<oxy:Plot Model="{Binding MyPlotModel}"/>
But you can figure out how to do the same with WinForms once (if?) you decide to use OxyPlot and import it.
I'm assuming you're doing some work that is related to times, but your code obviously doesn't say so ... you could play around with the top header, and maybe set how to show the numbers (ATM, with no zoom, they overlap each other a bit. zooming with scroller solves that, but that's just because i've set the tick size to 3600 ... )
In ZedGraph, how do I draw a time (like 00:00, 02:00, 04:00, etc.) on the Y axis and date (like 12-Apr-11, 13-Apr-11, 14-Apr-11, etc.) on the X axis?
The bar settings has been set to BarType.Stack.
Sample code will be very helpful.
Here is a sample that I constructed. I was not sure what sort of data you would plot along the Y Axis using a time format except for something like an accrued amount of time (such as number of hours employees worked).
ZedGraph uses an XDate format for time along the axes, which are doubles converted from datetimes. However in a stacked bar, I am not sure if ZedGraph can aggregate the times properly (I couldn't get it to work). Thus, in my example I used a Linear type for the Y Axis and changed the format so that it displays as hours and minutes.
Note that the min and max of both axes' scales have been set. This is especially important along the X axis, as the auto setting gets it wrong. Some of the other settings I specify clean up the minor tic marks, etc.
Here's an example showing a stacked bar graph for number of hours worked by three employees during each day:
const int NumberOfBars = 5;
GraphPane myPane = zedGraphControl1.GraphPane;
myPane.Title.Text = "Employee Hours";
myPane.BarSettings.Type = BarType.Stack;
myPane.BarSettings.ClusterScaleWidth = 1D;
// X AXIS SETTINGS
myPane.XAxis.Title.Text = "Date";
myPane.XAxis.Type = AxisType.Date;
myPane.XAxis.Scale.Format = "dd-MMM-yy";
myPane.XAxis.Scale.MajorUnit = DateUnit.Day;
myPane.XAxis.Scale.MajorStep = 1;
myPane.XAxis.Scale.Min = new XDate(DateTime.Now.AddDays(-NumberOfBars));
myPane.XAxis.Scale.Max = new XDate(DateTime.Now);
myPane.XAxis.MajorTic.IsBetweenLabels = true;
myPane.XAxis.MinorTic.Size = 0;
myPane.XAxis.MajorTic.IsInside = false;
myPane.XAxis.MajorTic.IsOutside = true;
// Y AXIS SETTINGS
myPane.YAxis.Title.Text = "Hours Worked";
myPane.YAxis.Type = AxisType.Linear;
myPane.YAxis.Scale.Format = #"00:\0\0";
myPane.YAxis.Scale.Min = 0;
myPane.YAxis.Scale.Max = 24;
myPane.YAxis.Scale.MajorStep = 1;
myPane.YAxis.MinorTic.Size = 0;
// Construct some sample data
Random r = new Random();
List<double> DatesX = new List<double>();
double[] JohnHours = new double[NumberOfBars];
double[] JoanHours = new double[NumberOfBars];
double[] JaneHours = new double[NumberOfBars];
for (int i = 0; i < NumberOfBars; i++)
{
DatesX.Add(new XDate(DateTime.Today.AddDays(-i)));
JohnHours[i] = r.Next(1, 9);
JoanHours[i] = r.Next(1, 9);
JaneHours[i] = r.Next(1, 9);
}
myPane.AddBar("John", DatesX.ToArray(), JohnHours, Color.Red);
myPane.AddBar("Joan", DatesX.ToArray(), JoanHours, Color.Blue);
myPane.AddBar("Jane", DatesX.ToArray(), JaneHours, Color.Green);