SciChart features a rich PointMarkers API to annotate the data-points of certain series with markers, e.g. Ellipse, Square, Triangle, Cross or a Custom Shape marker. Some series types, such as SCIXyScatterRenderableSeries or SCIFastImpulseRenderableSeries, require a PointMarker assigned to them unless they won’t render at all.
This article is about how to configure and add PointMarkers to a ISCIRenderableSeries to render markers for every data point.
It is possible to change how point markers appears by extending any of the above classes. The SCISpritePointMarker allows to render point markers on a SCIBitmap (which is a simple wrapper around the CGContext). For more details, refer to the Custom PointMarkers section down the page.
All the PointMarker types conforms to the ISCIPointMarker protocol, which provides the following properties for styling point markers:
Code for creation and assigning a PointMarker to a ISCIRenderableSeries is essentially the same regardless of a PointMarker type.
After an instance of it has been created, it can be configured and then applied to the ISCIRenderableSeries.pointMarker property:
// Create an Ellipse PointMarker instance
let pointMarker = SCIEllipsePointMarker()
pointMarker.size = CGSize(width: 40, height: 40)
pointMarker.strokeStyle = SCISolidPenStyle(color: .green, thickness: 2.0)
pointMarker.fillStyle = SCISolidBrushStyle(color: .red)
// Apply the PointMarker to a LineSeries
let lineSeries = SCIFastLineRenderableSeries()
lineSeries.pointMarker = pointMarker
// Create an Ellipse PointMarker instance
var pointMarker = new SCIEllipsePointMarker();
pointMarker.Size = new CGSize(40, 40);
pointMarker.StrokeStyle = new SCISolidPenStyle(UIColor.Green, 2.0f);
pointMarker.FillStyle = new SCISolidBrushStyle(UIColor.Red);
// Apply the PointMarker to a LineSeries
var lineSeries = new SCIFastLineRenderableSeries();
lineSeries.PointMarker = pointMarker;
The code above will produce the following chart (assuming that the data has been added to the Line Series):
Custom PointMarkers
There are two ways of creating custom PointMarkers in SciChart. The first one involves using our RenderContext2D API for drawing, and the second allows to use CoreGraphics drawing capabilities.
class EllipsePointMarker: SCIDrawablePointMarker
{
public override void InternalDraw(IISCIRenderContext2D renderContext, CGPoint point, IISCIPen2D pen, IISCIBrush2D brush)
{
renderContext.DrawEllipseAt(point, this.Size, pen, brush);
}
}
However, the RenderContext2D API has its own limitations. It isn’t suitable for drawing complex custom shapes.
Besides, calling drawing methods for every data point is redundant and is an overkill. So the second technique, described in the following paragraph, is better suited for most cases.
Implement ISCISpritePointMarkerDrawer
This approach is rather different. It allows to draw a shape on CoreGraphics CGContext.
Then, a sprite is created out of it, which is rendered for every data point in a series.
class CustomPointMarkerDrawer: ISCISpritePointMarkerDrawer {
let image: UIImage
init(image: UIImage) {
self.image = image
}
func onDraw(_ bitmap: SCIBitmap!, with penStyle: SCIPenStyle!, andBrushStyle brushStyle: SCIBrushStyle!) {
bitmap.context.saveGState()
let rect = CGRect(origin: .zero, size: CGSize(width: CGFloat(bitmap.width), height: CGFloat(bitmap.height)))
bitmap.context.translateBy(x: 0.0, y: CGFloat(bitmap.context.height))
bitmap.context.scaleBy(x: 1.0, y: -1.0)
bitmap.context.draw(image.cgImage!, in: rect)
bitmap.context.restoreGState()
}
}
…
let pointMarker = SCISpritePointMarker(drawer: CustomPointMarkerDrawer(image: #imageLiteral(resourceName: “image.weather.storm”)))
pointMarker.size = CGSize(width: 40, height: 40)
// Apply the PointMarker to a LineSeries
let lineSeries = SCIFastLineRenderableSeries()
lineSeries.pointMarker = pointMarker
class CustomPointMarkerDrawer : IISCISpritePointMarkerDrawer
{
public UIImage image { get; }
public IntPtr Handle => throw new NotImplementedException();
public CustomPointMarkerDrawer(UIImage image)
{
this.image = image;
}
public void onDraw(SCIBitmap bitmap, SCIPenStyle penStyle, SCIBrushStyle brushStyle)
{
bitmap.Context.SaveState();
var rect = new CGRect(CGPoint.Empty, new CGSize(bitmap.Width, bitmap.Height));
bitmap.Context.TranslateCTM((nfloat)0.0, bitmap.Height);
bitmap.Context.ScaleCTM((nfloat)1.0, (nfloat)(-1.0));
bitmap.Context.DrawImage(rect, image.CGImage);
bitmap.Context.RestoreState();
}
}
…
var pointMarker = new SCISpritePointMarker(new CustomPointMarkerDrawer(new UIImage(“image.weather.storm”)));
pointMarker.Size = new CGSize(40, 40);
// Apply the PointMarker to a LineSeries
var lineSeries = new SCIFastLineRenderableSeries();
lineSeries.PointMarker = pointMarker;