Add multiple annotations to a Candlestick chart - ios

I have a candle stick plot for a financial time series. Lets say there are 100 candles on the chart, based on certain criteria I short-list 20 points to annotate (i.e. show an image near the corresponding candle). All 20 points should carry the annotation simultaneously. A single annotation can be added, like so :
//the image to display
UIImage *flag = [UIImage imageNamed:#"flag.png"];
CPTImage *flagImage = [CPTImage imageWithCGImage:flag.CGImage
scale:flag.scale];
CPTBorderedLayer *borderedLayer = [[CPTBorderedLayer alloc] init];
CPTMutableLineStyle *blackLineStyle = [CPTMutableLineStyle lineStyle];
[blackLineStyle setLineColor:[CPTColor blackColor]];
[blackLineStyle setLineWidth:1.0f];
[borderedLayer setBorderLineStyle:blackLineStyle];
[borderedLayer setFill:[CPTFill fillWithImage:flagImage]];
//the 'i' in the next statement is from a 'for' loop iterating over NSArrays: xCoordinates & yCoordinates, containing the corresponding coordinates for short-listed data points
NSArray *anchorPoint = [NSArray arrayWithObjects:[xCoordinates objectAtIndex:i], [yCoordinates objectAtIndex:i], nil];
CPTPlotSpaceAnnotation *alertAnnotation = [[CPTPlotSpaceAnnotation alloc] initWithPlotSpace:[[[self hostView] hostedGraph] defaultPlotSpace]
anchorPlotPoint:anchorPoint];
[alertAnnotation setContentLayer:borderedLayer];
[alertAnnotation setDisplacement:CGPointMake(0.0f, 10.0f)];
[[[[self hostView] hostedGraph] plotAreaFrame] addAnnotation:alertAnnotation];
The problem is that only one CPTPlotSpaceAnnotation can be hosted at a time, so each time I loop through and perform addAnnotation: again the previous annotation is lost. Based on my research it figures that a CPTLayer (or subclass) has to be added for each annotation separately. Even after multiple attempts, I am unable to correctly add a layer and a corresponding image annotation to it on the chart. I unsuccessfully added multiple CPTLayers (using calls to addSublayer:) but they were not part of the Plot-space (i.e. they did not zoom or pan with the chart, which is the desired behaviour in my case)
Can someone help with how to achieve this behaviour? If needed, I will be happy to furnish more information about the problem/code.

There is no limit to the number of annotations you can add to a layer. Set the size of the annotation content layer to make sure it is visible. Create it with -initWithFrame: or set the bounds later.

Related

Can OHLC plots have up/down colour fills like Candle Stick increase/ decrease fills?

I am using Core-Plot to draw some financial time-series plots. The user can choose between candlestick or OHLC plots to draw. To distinguish between 'up'/'down' movements different fill colours can be set for a candlestick plot, like so :
//have included only some part of the relevant initialisation code for brevity
CPTTradingRangePlot *candlestickPlot = [[CPTTradingRangePlot alloc] init];
[candlestickPlot setPlotStyle:CPTTradingRangePlotStyleCandleStick];
[candlestickPlot setIncreaseFill:[CPTFill fillWithColor:[CPTColor greenColor]]];
[candlestickPlot setDecreaseFill:[CPTFill fillWithColor:[CPTColor redColor]]];
CPTTradingRangePlot *ohlcPlot = [[CPTTradingRangePlot alloc] init];
[ohlcPlot setPlotStyle:CPTTradingRangePlotStyleOHLC];
[ohlcPlot setIncreaseFill:[CPTFill fillWithColor:[CPTColor greenColor]]];
[ohlcPlot setDecreaseFill:[CPTFill fillWithColor:[CPTColor redColor]]];
Can the colour of 'up'/'down' movements be changed for the OHLC plot? Currently, as shown in the code above, setting the increase/decrease fill does not have any affect on it.
If needed, I will be happy to share more code or information about the problem.
This isn't supported in Core Plot right now. Please open an enhancement request on the issue tracker.

Getting bar chart height in Core-Plot

I am trying to make stack bar graph with use Core-Plot.
In last station,labels are not centered. I thought, if i can find height of each bar, i can place labels to the center of bars. How can i find height of bars? Or is there any easy way place the labels center of bars?
Thanks for your answer and advice.Sorry for my bad english :)
You can use plot space annotations instead of data labels. They will move with the plots if you pan and zoom. The center of a bar is easy to calculate since the annotation is positioned in data coordinates. Many of the example apps add annotations to scatter plots in response to user action. The same annotation code will work with any plot type.
For example, the center of the first green bar is at (5, 3.5).
NSArray *anchorPoint = #[#5, #3.5];
CPTTextLayer *textLayer = [[CPTTextLayer alloc] initWithText:#"Label Text"
style:[CPTTextStyle textStyle]];
textAnnotation = [[CPTPlotSpaceAnnotation alloc] initWithPlotSpace:graph.defaultPlotSpace
anchorPlotPoint:anchorPoint];
textAnnotation.contentLayer = textLayer;
[plot addAnnotation:textAnnotation];

Core Plot: Add text label to padded area around plotAreaFrame

I am creating a bar graph and then adding empty space above as follows....
CPTGraph *graph = self.hostView.hostedGraph;
graph.plotAreaFrame.paddingTop = smallPlotTopOffset; //some number like 25.0
I have already added paddingTop to the graph itself, and then used titleDisplacement to move the graph.title into this space.
However, now I would like to add a label between the title and the graph, in the space created by the plotAreaFrame.paddingTop. Is this possible using CPTTextLayers? I've been trying to add a layer, but they seem constrained by the plotAreaFrame (i.e. within the padding). I would like to align my label centred below my graph.title hence trying to use the hostView, rather than just adding a UILabel to the superview.
For anyone else interested I did this as follows....
CPTLayerAnnotation *ann = [[CPTLayerAnnotation alloc] initWithAnchorLayer:self.hostView.hostedGraph.plotAreaFrame];
ann.rectAnchor = CPTRectAnchorTop; //to make it the top centre of the plotFrame
ann.displacement = CGPointMake(0, -20.0); //To move it down, below the title
CPTTextLayer *textLayer = [[CPTTextLayer alloc] initWithText:#"Your Text" style:nil];
ann.contentLayer = textLayer;
[self.hostView.hostedGraph.plotAreaFrame addAnnotation:ann];
I think this is the best way, although it'd be better if I could anchor it to below the main title, rather than add a manual displacement.
Use a layer annotation and add it to the graph.

Add Multiple Annotations/Labels to CandlestickPlot in CorePlot

I am using Coreplot library to show a CandlestickPlot. The default implementation only has a single CPTPlotSpaceAnnotation attached to each plot in the graph(Highest value in OHLC/CandleStickPlot). I want to show two values for High as well as Low.
I tried adding below code but no luck.
CPTPlotSpaceAnnotation *labelAnnotation = [[CPTPlotSpaceAnnotation alloc] initWithPlotSpace:self.plotSpace anchorPlotPoint:[NSArray arrayWithObjects:newX, newY, nil]];
labelAnnotation.annotationHostLayer = label.annotationHostLayer;
labelAnnotation.contentLayer = label.contentLayer;
[self addAnnotation:labelAnnotation];
[label is the default annotation displayed]
An annotation cannot share a content layer with another annotation. You'll need to create a new layer for the annotation content. CPTTextLayer is a common choice, but it can be any CPTLayer.
What is self in this context? The default plot data label annotations are added to the plot; you should add your secondary plot labels to the plot, too.

Core Plot: should adding a CPTPlotSpaceAnnotation to plot space cause the entire graph to be redrawn?

I am using Core Plot to display a time series of prices. When the user touches the graph, I display a draggable vertical line at that point. Both the time series and the draggable line are CPTScatterPlot objects within a CPTXYGraph. This works pretty well - performance as you drag the line across the time series graph is acceptable.
The next stage is to display the price and date at the point that the user has selected. The Yahoo Stocks App has a nice feature which displays the price in a label that moves as if it is attached to the top of the draggable line. I have tried to replicate this using text displayed in a CPTPlotSpaceAnnotation. This works, but it severely impacts performance. After some digging, I found that CPTLayer drawInContext: is being called multiple times - it looks like the entire graph is being redrawn each time I redraw the text label (in fact my logs imply it's being redrawn twice).
Here is the code which draws the label (work in progress). It is called by plotSpace:shouldHandlePointingDeviceDraggedEvent:atPoint:.
- (void)displayPriceAndDateForIndex:(NSUInteger)index atPoint:(CGPoint)pointInPlotArea
{
NSNumber * theValue = [[self.graphDataSource.timeSeries objectAtIndex:index] observationValue];
// if the annotations already exist, remove them
if ( self.valueTextAnnotation ) {
[self.graph.plotAreaFrame.plotArea removeAnnotation:self.valueTextAnnotation];
self.valueTextAnnotation = nil;
}
// Setup a style for the annotation
CPTMutableTextStyle *annotationTextStyle = [CPTMutableTextStyle textStyle];
annotationTextStyle.color = [CPTColor whiteColor];
annotationTextStyle.fontSize = 14.0f;
annotationTextStyle.fontName = #"Helvetica-Bold";
// Add annotation
// First make a string for the y value
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setMaximumFractionDigits:2];
NSString *currentValue = [formatter stringFromNumber:theValue];
NSNumber *x = [NSNumber numberWithDouble:[theDate timeIntervalSince1970]];
NSNumber *y = [NSNumber numberWithFloat:self.graphDataSource.maxValue];
NSArray *anchorPoint = [NSArray arrayWithObjects:x, y, nil];
// Then add the value annotation to the plot area
float valueLayerWidth = 50.0f;
float valueLayerHeight = 20.0f;
CPTTextLayer *valueLayer = [[CPTTextLayer alloc] initWithFrame:CGRectMake(0,0,valueLayerWidth,valueLayerHeight)];
valueLayer.text = currentValue;
valueLayer.textStyle = annotationTextStyle;
valueLayer.backgroundColor = [UIColor blueColor].CGColor;
self.valueTextAnnotation = [[CPTPlotSpaceAnnotation alloc] initWithPlotSpace:self.graph.defaultPlotSpace anchorPlotPoint:anchorPoint];
self.valueTextAnnotation.contentLayer = valueLayer;
// modify the displacement if we are close to either edge
float xDisplacement = 0.0;
...
self.valueTextAnnotation.displacement = CGPointMake(xDisplacement, 8.0f);
[self.graph.plotAreaFrame.plotArea addAnnotation:self.valueTextAnnotation];
// now do the date field
...
}
Is the full redraw expected behaviour? And is there a better way of managing the annotation without destroying it and recreating it each time the method is called?
You shouldn't need to destroy and create the annotation every time. Once it has been created, just update the anchorPoint. Removing and adding the annotation is probably related to the constant redraw.

Resources