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;
Related
I have a chart in windows forms and I want the gridlines to be squared. The gridline is anchored to bottom, top, left and right so it resizes with the screen. How do I make the grid lines always square and make the whole chart resize with the screen?
I have tried setting the width and height to be the same, but it doesn't work since the series names of the chart are on the right.
EDIT 1:
Here is the full uncensored code:
chart1.ChartAreas[0].AxisY.Minimum = 0;
chart1.ChartAreas[0].AxisY.Maximum = max;
chart1.ChartAreas[0].AxisX.Minimum = 0;
chart1.ChartAreas[0].AxisX.Maximum = max;
chart1.ChartAreas[0].AxisX.LabelStyle.Format = "0";
chart1.ChartAreas[0].AxisY.LabelStyle.Format = "0";
chart1.ChartAreas[0].AxisX.Interval = 1;
chart1.ChartAreas[0].AxisY.Interval = 1;
chart1.ChartAreas[0].AxisX.IntervalAutoMode = IntervalAutoMode.FixedCount;
chart1.ChartAreas[0].RecalculateAxesScale();
for (int i = 0; i < points.ToArray().Length; i++)
dt.Rows.Add(pointsArr[i, 0], pointsArr[i, 1]);
chart1.DataSource = dt;
chart1.Series["תחום הפתרונות האפשריים"].BorderWidth = 0;
float[] OptimalPoint = CalculateOptimalPt(convertEq(z), ListArToAr(points));
if (OptimalPoint[0] == 0)
{
for (int i = 0; i < 2; i++)
{
DataPoint dp = new DataPoint();
dp.SetValueXY(i, OptimalPoint[1]);
if (i > 0) dp.Color = Color.Transparent;
chart1.Series["פיתרון אופטימלי"].Points.Add(dp);
}
}
else
chart1.Series["פיתרון אופטימלי"].Points.AddXY(OptimalPoint[0], OptimalPoint[1]);
chart1.Series["פיתרון אופטימלי"].Points[0].MarkerSize = 10;
chart1.Series["תחום הפתרונות האפשריים"].XValueMember = "X_Value";
chart1.Series["תחום הפתרונות האפשריים"].YValueMembers = "Y_Value";
chart1.Series["תחום הפתרונות האפשריים"].ChartType = SeriesChartType.Area;
panel1.Visible = false;
panel2.Visible = true;
You can do it by anchoring the chart only to the Top and Left and calculating and setting the Width and Height yourself when Form size changes.
To do so we get fundamental data of the chart in the form constructor.
private readonly Size _innerMargin = new Size(183, 55); // Estimated
private readonly Size _outerMargin;
private readonly float _aspectRatio;
public Form1()
{
InitializeComponent();
_outerMargin = Size - chart1.Size;
Size innerSize = chart1.Size - _innerMargin;
_aspectRatio = (float)innerSize.Width / innerSize.Height;
}
_innerMargin is the estimated total difference between the chart size and the plot area with the gridlines. I actually got it from a screenshot and measured it in a graphics application.
_outerMargin is the difference of the form size and the chart control size.
This calculation of the initial _aspectRatio assumes the grid lines build perfect squares when the form opens. Instead, you could set this aspect ratio from the known number of squares in X and Y:
_aspectRatio = 16f / 16f; // From your example image.
In the Form_Resize event handler, we then set the new size of the chart. Depending on whether the current aspect ratio (calculated from the theoretical new maximum plot area size) is less than or greater than the original aspect ratio, the height or the width of the chart determines the maximum chart size. The other dimension must be calculated so that the aspect ratio of plot area remains the same.
private void Form1_Resize(object sender, EventArgs e)
{
Size maxChartSize = Size - _outerMargin;
Size innerSize = maxChartSize - _innerMargin;
double currentAspectRatio = (float)innerSize.Width / innerSize.Height;
if (currentAspectRatio < _aspectRatio) {
int chartWidth = Width - _outerMargin.Width;
chart1.Width = chartWidth;
chart1.Height = (int)((chartWidth - _innerMargin.Width) / _aspectRatio + _innerMargin.Height);
} else {
int chartHeight = Height - _outerMargin.Height;
chart1.Height = chartHeight;
chart1.Width = (int)((chartHeight - _innerMargin.Height) * _aspectRatio + _innerMargin.Width);
}
}
I have a 6300 * 5 array with:
Columns 1,2 = CIE data
Columns 3,4,5 = S R G B
How should I Draw this in MsChart?
You have several options:
Add DataPoints with Markers in the respective Colors
Add Annotations
Use one of the xxxPaint events
With only 6500 points you can't really fill the area by setting single pixels. So you better use a FillElipse call for each point.
If you use the Pre- or PostPaint event you will need to use the AxisX/Y methods ValueToPixelPosition for calculating the pixel coordinates from the CIE values.
In any case you set the Minimum and Maximum for both Axes.
Also you will need to calculate either the Markers' or the Annotations' or the ellipses' size from the chart's ClientSize to avoid ugly gaps in the colored area.
If you want to use DataPoints set the ChartType = Point and use this function for each of your data:
DataPoint Cie2DataPoint(float x, float y, float r, float g, float b)
{
var dp = new DataPoint(x, y);
dp.Color = Color.FromArgb((int)(256 * r), (int)(256 * g),(int)(256 * b));
dp.MarkerColor = dp.Color;
return dp;
}
Here are examples of helper function:
int MarkerSize(Chart chart, int count)
{
return Math.Max(chart.ClientSize.Width, chart.ClientSize.Height )/ count + 1
}
void Rescale(Chart chart)
{
Series s = chart3.Series[0];
s.MarkerSize = MarkerSize(chart3, (int)Math.Sqrt(s.Points.Count));
}
The former takes an estimate of how many plot points you expect per axis; you may need to experiment a little. The next one assumes the points are actually filling a square; also that you only have one ChartArea.
This should also be modified for your data!
We need to rescale the sizes when the Chart is resized:
private void chart3_Resize(object sender, EventArgs e)
{
Rescale (sender as Chart);
}
Here is an example of setting it up with a calculated set of data. You should loop over your list of data instead..:
Series s = chart3.Series[0];
s.ChartType = SeriesChartType.Point;
s.MarkerSize = 3;
for (int x = 0; x < 100; x++)
for (int y = 0; y < 100; y++)
{
s.Points.Add(Cie2DataPoint(x/100f, y/100f, x/100f, y/100f, (x+y)/200f));
}
ChartArea ca = chart3.ChartAreas[0];
ca.AxisX.Minimum = 0;
ca.AxisY.Minimum = 0;
ca.AxisX.Maximum = 1;
ca.AxisY.Maximum = 1;
ca.AxisX.Interval = 0.1f;
ca.AxisY.Interval = 0.1f;
ca.AxisX.LabelStyle.Format = "0.00";
ca.AxisY.LabelStyle.Format = "0.00";
Rescale(chart3);
Result:
After grabbing ~6k colors from a CIE color chart the result looks rather grainy but basically correct:
Note that you probably need to allow for the reversed y-axis somehow; I simply subtracted my y-values from 0.9f. Use your own numbers!
I am working on GIS based desktop application using C#. I am using dotspatial library in this project.
Now I need to create a grid of features on polygon. This grid cell (rectangle) should be 20*20 Meter Square.
I have worked on it and able to create grid but facing issue regarding to cell size. Whenever polygon size changed cell size also reduced. My code.
// Polygon Width = 2335
// Polygon Height = 2054
int RowsCount = 111;
int ColumnsCount = 111;
var maxPointX = Polygon.Extent.MaxPointX;
var minPointX = Polygon.Extent.MinPointX;
var maxPointY = Polygon.Extent.MaxPointY;
var minPointY = Polygon.Extent.MinPointY;
var dXStep = (maxPointX - minPointX) / (ColumnsCount - 1);
var dYStep = (maxPointY - minPointY) / (RowsCount - 1);
var gridColumnsPoints = new double[1000000];
var gridRowPoints = new double[1000000];
// Calculate the coordinates of grid
var nextPointX = minPointX;
for (int i = 1; i <= ColumnsCount; i++)
{
gridColumnsPoints[i - 1] = nextPointX;
nextPointX = nextPointX + dXStep;
}
var nextPointY = minPointY;
for (int i = 1; i <= RowsCount; i++)
{
gridRowPoints[i - 1] = nextPointY;
nextPointY = nextPointY + dYStep;
}
Output
Now when I tried this code on small size of Polygon then grid cell size also decreased.
I know my approach is not correct, So I have searched on it and got some tools. like
https://gis.stackexchange.com/questions/79681/creating-spatially-projected-polygon-grid-with-arcmap
But I want to create it in C# and unable to found any algorithm or any other helping material.
Please share your knowledge. Thanks
I am not able to understand, if you want the grid cell size to be 20*20 meters, how does the size change from polygon to polygon. It should always be 20*20 meters.
In you code, where did you get the values for ColumnsCount and RowsCount?
Your dx and dy should always be 20 (if the spatial reference units are in meters) or you need to convert the 20 meters to appropriate length of units of the spatial reference.
Pseudo code for creating grid:
var xMax = Polygon.extent.xmax;
var xMin = Polygon.extent.xmin;
var yMax = Polygon.extent.ymax;
var yMin = Polygon.extent.ymin;
var gridCells = [];
var x = xMin, y = yMin;
while(x <= xMax){
var dx = x + 20;
while(y <= yMax){
var dy = y + 20;
var cell = new Extent(x, y, dx, dy);
gridCells.push(cell);
y = dy;
}
x = dx;
}
The problem is here:
var dXStep = (maxPointX - minPointX) / (ColumnsCount - 1);
var dYStep = (maxPointY - minPointY) / (RowsCount - 1);
because it makes the grid size dependent on the polygon, but it should be fixed to the scale of the view.
I'm not familiar with the dotspatial framwork, but you must operate in a coordinate system of a kind. You should align your grid to that coordinate system by calculating the first x pos to the left of the polygon in some distance from the polygons bounding box (max/min) and then step with the resolution of the coordinate system through to the max X of the polygon.
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.
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);