Introduction
I'm building an application using Xamarin Forms. I wanted to have an interface showing the relative position of two objects, my device and another object with GPS. In most of the cases, I will use a map such as Google map to show the location of the other object but how could I provide any information to the users even without Internet access.
Using the Code
This tip shows how to build a radar using SKCanvasView
, a control belonging to SkiaSharp.Views.Forms
that is a library in Xamarin.Form
; but the same logic can be apply to any other canvas
such as HTML5 Canvas
.
To start, I add the control to my interface:
<skia:SKCanvasView x:Name="CanvasBase" PaintSurface="OnCanvasViewPaintSurface"
VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
</skia:SKCanvasView>
The attribute PaintSurface
is attached to a handler OnCanvasViewPaintSurface
that takes care of drawing the elements of the radar in the canvas
.
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
int margin = 6;
canvas.Clear();
float side = ((info.Height < info.Width) ? info.Height : info.Width) - margin;
using (SKPaint paint = new SKPaint
{ Style = SKPaintStyle.Stroke,
Color = Color.Black.ToSKColor(),
StrokeWidth = 10
}) {
canvas.DrawCircle(info.Width / 2, info.Height / 2, side / 2, paint);
canvas.DrawLine(info.Width / 2, (info.Height - side)/ 2, info.Width / 2,
info.Height - (info.Height - side) / 2, paint);
canvas.DrawLine((info.Width - side) / 2, info.Height / 2,
info.Width - (info.Width - side) / 2, info.Height / 2, paint);
}
using (SKPaint paintPositionOne = new SKPaint
{ Style = SKPaintStyle.Fill,
Color = Color.Blue.ToSKColor(),
StrokeWidth = 10
}) {
canvas.DrawCircle(info.Width / 2, info.Height / 2, 20, paintPositionOne);
}
double distance = CalculateDistance(positionOne, positionTwo);
int scale = CalculateScale(distance);
Tuple<float, float> point = CalculatePoint(positionOne, positionTwo, scale);
using (SKPaint paintText = new SKPaint
{ Style = SKPaintStyle.StrokeAndFill,
Color = Color.Black.ToSKColor(),
StrokeWidth = 1, TextSize = 30
}) {
canvas.DrawText("N", new SKPoint((info.Width / 2) + 10, 0 + 40), paintText);
canvas.DrawText(scale + " meters", new SKPoint((info.Width * 3 / 4) + 60,
(info.Height * 1 / 4) - 60), paintText);
}
float x = (info.Width / 2) + (point.Item1 * size / 2);
float y = (info.Height / 2) - (point.Item2 * size / 2);
using (SKPaint paintPositionTwo = new SKPaint
{ Style = SKPaintStyle.Fill,
Color = Color.Red.ToSKColor(),
StrokeWidth = 10
}) {
canvas.DrawCircle(x, y, 20, paintPositionTwo);
}
}
First, I clear the canvas
.
The radar needs to be square and leave a margin relative to its parent. I calculate its dimension and call it side.
I draw a circle centered in the canvas
and having half of the size as radius.
Next, I draw a vertical line horizontally centered keeping the margins and a horizontal line with similar conditions.
I draw a point in the middle to indicate my device position, I'm always in the center.
In order to draw a point to indicate the other object position, I need to calculate the distance between the two objects. I used Xamarin.Essentials
library to handle the GPS of the device and to calculate distances.
Having this distance, I calculate the scale of the radar. I add a label with the estimated distance from me to the circle I previously draw.
Next, I need to calculate the distance between the two objects in the axis X and Y; before I calculated the direct distance - the hypotenuse. So, I use the same library that handles the GPS to calculate the distance between the two points projected to the same X and projected to the same Y. Also, these distances need to be adjusted to the current scale.
To locate the point for the other object, I start horizontally centered and add the distance in the X and vertically centered and remove the distance in the Y, because the X in the canvas
grows to the right but the Y in the canvas
grows to the bottom opposite to the Y axis normal behavior.
Finally, I draw a label N at the top indicating the north. Canvas
could be rotated with information received from a compass if this feature is support by the device.
History
- 26th October, 2018: Initial version