I work with UWP MapControl and adding some MapPolylines.
And they looks ugly (see pic below)
I assume should be kind of antialiasing property but cannot find it here.
Please help and thank you!
C#
var mapPolyline = new MapPolyline();
var geoPositions = new List<BasicGeoposition>();
foreach (var vertex in polyLine.Vertex)
{
// adding BasicGeopositions...
};
mapPolyline.StrokeColor = Colors.Black;
mapPolyline.StrokeThickness = 1;
mapPolyline.Path = new Geopath(geoPositions);
((MapElementsLayer)impotMapLayer).MapElements.Add(mapPolyline);
UPDATE #1 based on the answer
I have investigated this article "Overlay tiled images on a map" and also
this one "MapTileBitmapRequestedEventArgs Class" and cannot get the clear definition of the X and Y of "MapTileBitmapRequestedEventArgs Class"
The article says
X Gets the X value of the requested tile.
Y Gets the Y value of the requested tile.
Using MSDN example from here I get following log for X, Y, Zoom
X 6073 Y 2617 Zoom 13
X 6072 Y 2616 Zoom 13
X 6071 Y 2615 Zoom 13
X 6071 Y 2617 Zoom 13
X 6072 Y 2614 Zoom 13
X 6073 Y 2615 Zoom 13
X 6071 Y 2616 Zoom 13
X 6073 Y 2614 Zoom 13
X 6072 Y 2615 Zoom 13
and etc
Would you mind clarify what those numbers are exactly and how I can associate it with geolocations set of vertices in memory if I want create tile-image only, please?
(My polylines set already calculated in geopoints.)
Thank you very much!
UPDATE #2 Here is the solution
First of all I read this article https://learn.microsoft.com/en-us/bingmaps/articles/bing-maps-tile-system?redirectedfrom=MSDN
So we need TileSystem to make series of convertions
namespace Microsoft.MapPoint
{
static class TileSystem
...
The X and Y of MapTileBitmapRequestedEventArgs are Tile's XY we have to pass to TileSystem.TileXYToPixelXY(args.X, args.Y, out int pixelX, out int pixelY);
The final code is following based on https://learn.microsoft.com/en-us/windows/uwp/maps-and-location/overlay-tiled-images
private async void customDataSource_BitmapRequestedAsync(CustomMapTileDataSource sender, MapTileBitmapRequestedEventArgs args)
{
var deferral = args.Request.GetDeferral();
TileSystem.TileXYToPixelXY(args.X, args.Y, out int pixelX, out int pixelY);
TileSystem.PixelXYToLatLong(pixelX, pixelY, args.ZoomLevel, out double lat, out double lng);
Debug.WriteLine($"lat {lat} lng {lng} Zoom {args.ZoomLevel}");
// next step is to extract from my custom array polylines accroding to TileSystem.PixelXYToLatLong
// and finally pass it inside of CreateBitmapAsStreamAsync(array to draw);
args.Request.PixelData = await CreateBitmapAsStreamAsync(array to draw);
deferral.Complete();
}
Currently MapPolylines are drawn without antialiasing and there is no setting to change that behavior.
Looking at your screenshot, you may be better served by using a custom tile layer. See CustoMapTileDatasource here: https://learn.microsoft.com/en-us/windows/uwp/maps-and-location/overlay-tiled-images
You can draw each tile in a callback using whatever method you like including antialiasing. It will also tend to perform better for a large collection of static lines like the topographic contours in your example
Related
I am working on a project which consists of drawing a line, BUT, the most important thing is that I need to get the Y position on every X position. So, for every X I need a Y. Drawing a line and getting the positions works fine while I draw slowly. But when I move the cursor faster, even if the line gets drawn, the positions are not saved for every X, and this is a problem. You can just take a look at the image below.
As you can see, the green line is the one that Unity provides, LineRenderer. Just to prove my problem, I have drawn a circle at every position saved on the Line. So, even though the line gets drawn, I can't save my positions. Is there a way to solve this? Thank you and have a great day!
If I understood your question correct now you want to find all pixels that lie on the lines build by the given points in an image.
Not fully sure since typing on the smartphone right now but I think you could do something like
// Expects givenPoints in pixel coordinates for positions to draw lines between
public List<Vector2Int> CalculateLinePixels(List<Vector2Int> givenPoints)
{
var output = new List<Vector2Int>();
// Already add the first point once
output.Add(givenPoints[0]);
// start with the second point and calculate the line points back
// between the current and the point before
for (var i = 1; i < givenPoints.Count; i++)
{
var startPoint = givenPoints[i - 1];
var endPoint = givenPoints[i];
// get the difference between them in pixels
var dif = endPoint - startPoint;
// Get the pixel step in Y per pixel step on X
var step = dif.y / (float)dif.x;
// go through all the pixels on the X axis between both points
// excluding the first as these should already match the
// startPoint (== last endPoint)
//
// Note that for now this assumes that the line goes always strict from left to right
// -> no forth and back drawing, no vertical drawing
for (var x = 1; x < dif.x; x++)
{
// every step in X add one step in Y starting from the first points Y
var y = Mathf.RoundToInt(step * x);
// Add the new line point
output.Add(startPoint + new Vector2Int(x, y));
}
}
return output;
}
As also commented in code:
Note that this assumes that the line goes always strict from left to right. => No forth and back drawing, no horizontal drawing.
For these you could add some additional checks such as if(dif.x < 0) then you would need to iterate in the other direction for (var x = -1; x > dif.x ; x--). And if it is if(dif.x == 0) you might want to rather only draw a horizontal line to the next point filling all above/below.
For better results you might want to look into Line drawing algorithms and implement a different way of how to obtain and fill the pixels differently.
If I understood correctly, your problem is caused by the fact that the coordinates are sampled rarely, you could just focus on getting the values while moving the cursor and then draw the line, it would be a faster way.
I want to make a chart in C# with custom elements. What I have:
What I want:
Elements marked by red circles need to be replaced by the image. My program code is very short, just some values for the chart. All settings for chart were set by the "Collection" in Chart section (as shown on first image).
This is a BoxPlot chart and it takes 6 y-values.
You can add them or let the chart calculate them.
Looks like you want to add several images to the various y-values..
Here is an example of how to do that by owner-drawing the Chart. (No, not the whole chart, just a little extra custom-drawing ;-)
It adds an image to each of the y-values; it should be easy to adapt to only those values you really want. And if you only want one you may even do away with the ImageList and pick the image from the resources; (although using an ImageList is a nice way, as long as you can live with the limitations of 256x256 maximum size and all images having the same size and color depth..)
You seem to want one of these only:
4 Average and mean
5 Median
private void chart_PostPaint(object sender, ChartPaintEventArgs e)
{
Series s1 = chart.Series[0];
ChartArea ca = chart.ChartAreas[0];
Axis ax = ca.AxisX;
Axis ay = ca.AxisY;
Graphics g = e.ChartGraphics.Graphics;
int iw = imageList1.ImageSize.Width / 2;
int ih = imageList1.ImageSize.Height / 2;
foreach (DataPoint dp in s1.Points)
{
int x = (int) ax.ValueToPixelPosition(dp.XValue);
for (int i = 0; i < 6; i++)
{
int y = (int) ay.ValueToPixelPosition(dp.YValues[i]);
g.DrawImage(imageList1.Images[i], x - iw, y - ih);
}
}
}
I suggest to use png files with transparency and an odd width so they look nice and centered. (I used randomly 16x16, which is not quite that nice ;-) - For this you need to set the ImageSize and the ColorDepth of the ImageList.
To further style the chart you may use these special properties
Custom attributes
BoxPlotPercentile, BoxPlotSeries, BoxPlotShowAverage,
BoxPlotShowMedian, BoxPlotShowUnusualValues, BoxPlotWhiskerPercentile,
DrawSideBySide, MaxPixelPointWidth, MinPixelPointWidth,
PixelPointDepth, PixelPointGapDepth, PixelPointWidth, PointWidth
Note that you need to set them all as strings, maybe like this:
s1.SetCustomProperty("someAttribute", "someValue");
I'm new to charts and have a line chart that looks as thus.
The vertical line is where the cursor is and updates via mousemove rather than mouseclick.
As the title suggests what I want to do is access the Y value at the point at which the vertical line and the 'data line' intersect.
I've tried this - Get Y value of series from the X Cursor position where a ChartArea is clicked on but unless I'm missing something it just doesn't work, it either returns the first or the last value in the series depending on which datapoint you use.
I've tried hittestresult, that only seems to work if you're 'touching' the data line itself.
Any ideas?
Since you didn't show us any code and didn't answer my questions I can only assume that your chart doesn't have valid, i.e. numeric x-values.
This means the the x-values are all 0 and can't be used for anything: neither for setting a zoom range, not for formatting axis or other labels and also not for finding the DataPoints at an x-position.
This can be called 'implicitly indexed'. The result is similar to explicitly indexed charts, which results from setting the IsXValueIndexed of a Series to true: The DataPoints are lined up in a row and all are displayed at the same distance.
This is usually not what one wants and I really suggest you fix it by adding the DataPoints not like this:
for (int i = 0; i < count; i++) chart1.Series[0].Points.AddY(someYValue);
but maybe like this:
for (int i = 0; i < count; i++) chart1.Series[0].Points.AddXY(i, someYValue);
Then the linked answer will work just fine.
But just to show how you could workaround here is how to find the two closest points in an indexed chart.
Note that it uses a function, (actually two) that calculates the pixel rectangle of the inner plot postion. You can find them here or here..:
private void chart1_MouseMove(object sender, MouseEventArgs e)
{
ChartArea ca = chart1.ChartAreas[0];
Series S = chart1.Series[0];
RectangleF rf = InnerPlotPositionClientRectangle(chart1, ca);
float px = (float)( (e.X - rf.X) * S.Points.Count / rf.Width );
int p0 = (int)px; // previous point
int p1 = p0 + 1; // next point
if (p0 >= 0 && p0 < S.Points.Count)
Console.WriteLine( "DataPoint # " + p0 + " has a y-value of " +
S.Points[p0].YValues[0].ToString("0.00"));
//..
}
It will work but you really should correct the way you add the data points!
So I use an open source .NET library for plotting: FPlot
Here's the function that I'm trying to draw:
f(x,y) = x^2+3*y^2+2*x*y
Here's what I want it to look like:
Clarification:
I don't need the exact same appearence as in the image, I just need the plot to be mathematically correct
There are only 10 conours in the picture, I need as much as can be fit on the screen
Here's how I tried to do this:
var graphFunction = new Function2D();
graphFunction.source = "return (pow(x,2)+3*pow(y,2)+2*x*y)/10;";
/* I'm dividing by 10 because otherwise the whole plot is solid color */
graphFunction.Compile(true);
That's how the FPlot generated plot looks up close:
This is exactly what I want, but when I zoom out here's what happens:
Theese extra ellipses are not supposed to be there, in fact they are not there, this is just a graphical artefact, because when you zoom into one of theese 'fake' ellipses this is what you see:
The problem can be in this line:
graphFunction.source = "return (pow(x,2)+3*pow(y,2)+2*x*y)/10;";
...or in the FPlot source code. Any Ideas?
UPDATE:
So, z value in graph seems to be the problem. When value of a function, z = f(x,y) in a graph exceeds the z1 (max z) value it resets to z = z%z1 (same happens when z is lower then z0), which causes these "lines" - they are not countour lines, like I thought.
So that means the solution is: set z0 to min f(x,y) on screen, and set z1 to max f(x,y) on screen.
Make the displaying borders of your FPlotLibrary.GraphControl have the same value in all 3 dimensions and the problem goes away:
graphControl1.x0 = -40;
graphControl1.x1 = 40;
graphControl1.y0 = -40;
graphControl1.y1 = 40;
graphControl1.z0 = -40;
graphControl1.z1 = 40;
Btw, the "problem" reproduces, for instance, if you do
graphControl1.x0 = -40;
graphControl1.x1 = 40;
graphControl1.y0 = -40;
graphControl1.y1 = 40;
graphControl1.z0 = -1;
graphControl1.z1 = 1;
I'm working with data (stuff like Sin/Cosin waves, etc) that repeat with frequency M.
I've written a simple display control where it takes the data and paints connected lines to represent the data in a pretty picture.
My question is, the data is given where if painted onto a bitmap, quadrant 1 data is in quadrant 3 and quadrant 2 data is in quadrant 4 (and vice versa).
The bitmap is of width M and hight array.Max - array.Min.
Is there a simple transform for changing the data so it will display in the appropriate quadrants?
A good way of thinking about it is that (0,0) in world coordinates is divided between
(0,0), (width, 0), (0,height), (width, height)
which would (width/2, height/2) in image coordinates.
From there, the transform would be:
Data(x,y) => x = ABS(x - (width/2)), y = ABS(y - (Height/2))
Graphics.ScaleTransform is not a good idea because it will affect not only layout but also drawing itself (thickness of strokes, texts and so on).
I suggest you to prepare points list and then perform a transformation to them using the Matrix class. This is a small example I made for you, hope it will be helpful.
private PointF[] sourcePoints = GenerateFunctionPoints();
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.Clear(Color.Black);
// Wee need to perform transformation on a copy of a points array.
PointF[] points = (PointF[])sourcePoints.Clone();
// The way to calculate width and height of our drawing.
// Of course this operation may be performed outside this method for better performance.
float drawingWidth = points.Max(p => p.X) - points.Min(p => p.X);
float drawingHeight = points.Max(p => p.Y) - points.Min(p => p.Y);
// Calculate the scale aspect we need to apply to points.
float scaleAspect = Math.Min(ClientSize.Width / drawingWidth, ClientSize.Height / drawingHeight);
// This matrix transofrmation allow us to scale and translate points so the (0,0) point will be
// in the center of the screen. X and Y axis will be scaled to fit the drawing on the screen.
// Also the Y axis will be inverted.
Matrix matrix = new Matrix();
matrix.Scale(scaleAspect, -scaleAspect);
matrix.Translate(drawingWidth / 2, -drawingHeight / 2);
// Perform a transformation and draw curve using out points.
matrix.TransformPoints(points);
e.Graphics.DrawCurve(Pens.Green, points);
}
private static PointF[] GenerateFunctionPoints()
{
List<PointF> result = new List<PointF>();
for (double x = -Math.PI; x < Math.PI; x = x + 0.1)
{
double y = Math.Sin(x);
result.Add(new PointF((float)x, (float)y));
}
return result.ToArray();
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
Invalidate();
}
Try to invert the y-axis using
g.ScaleTransform(1, -1);
Also remember that for drawing in a scaled context, if you have to, Pen for example takes width as Single in some of its constructors, meaning inversely proportional fractional values can be used to make an invariant compensation for the effect of ScaleTransform.
UPDATE: forget that, Pen has its own local ScaleTransform, so both x an y can be compensated for.