How to highlight missing data - ios

I have an obj-c iOS app that use CorePlot to print a chart of computed angles. x-axis for angle domain and y-axis for time. The chart change during time scrolling from top to bottom in order to show the degree. The y-axis is centred on mean value of these angle array. The data source i s NSMutableArray that a contains degrees.
An external module, every second, analyse raw data in order to generate a new computed angle to show in chart. The chart is updated very second and the y-axis scroll down showing the flow of time (something like CorePlot demo project with timer for continuous scrolling data but vertical not horizontal).
Sometimes this operation cannot generate (the angle is the same as the last one computed) a new angle but I still have to update the chart to show the flow of time. The question are:
1) Can I change color for these point in orde to highlight missing data for this range of time?
2) Can I highlight this area to show where the data is not present? Something like this (but vertical style)
https://keystrokecountdown.com/articles/corePlot/index.html
This is my current implementation:
1) Configuring plot area (some graphics setup)
- (void)initPlot {
[self configureHost];
[self configureGraph];
[self configurePlots];
[self configureAxes];
}
2) Data source. Select data from data source (array) to show in chart. x=array value, y=array index
#pragma mark - Core Plot Data Source
- (NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot {
if ([((NSString *)plot.identifier) isEqualToString:#"MY_ID"]) {
return [degreesArray count];
}
}
- (NSNumber *)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index {
NSNumber *num;
if ([((NSString *)plot.identifier) isEqualToString:#"MY_ID"]) {
if (fieldEnum == CPTScatterPlotFieldX) {
num = [degreesArray objectAtIndex:index];
} else {
num = [NSNumber numberWithInt:(int)index + currentIndex - (int)degreesArray.count];
}
}
return num;
}
3) Updating chart with new angle
- (void)newSetData:(int)newData {
// Check the correct plot
CPTGraph *leftGraph = self.leftPlot.hostedGraph;
CPTPlot *leftPlot = [leftGraph plotWithIdentifier:#"MY_ID"];
if (leftPlot) {
if (degreesArray.count > kDataOnGraph) {
NSRange range;
range.location = 0;
range.length = degreesArray.count-kDataOnGraph;
[degreesArray removeObjectsInRange:range];
[leftPlot deleteDataInIndexRange:range];
}
// Updating y axis range
[self updateSetYAxis];
currentIndex++;
newAngle = ...;
// Saving new SET data
[degreesArray addObject:[NSNumber numberWithInt:(int)newAngle]];
lastAngle_ = [NSNumber numberWithInt:newAngle];
// Adding new SET data to chart
[leftPlot insertDataAtIndex:degreesArray.count - 1 numberOfRecords:1];
// Updating axis
[self updateSetXAxis];
[self updateSetLabels];
}
}

Related

CorePlot: How to add a plotting point to CurvedScatterPlot?

I am using CorePlot for the first time now for graphs. My graph is showing ok. Everything is drawn ok. I was searching but I could not find answer to this question: how to mark maximum and minimum point on my graph with a plotting point and how to show that value above or below that point? I do not want to show value of every point. I've seen some comments to use this:
-(CPTLayer *)dataLabelForPlot:(CPTPlot *)plot recordIndex:(NSUInteger)index
but how to use that thing?
EDIT: I've added plotting points using CPTPlotSymbol. The problem is, I want to add CPTPlotSymbol only for two values. How to do that?
You can plot different plot symbols at each point on a scatter plot. Implement the -symbolForScatterPlot:recordIndex: datasource method and return the appropriate symbol for each data index. Return [NSNull null] if you don't want a symbol at that index. Return nil to use the plot's plotSymbol at that index.
In preparing your plot, find and store indices of min/max points:
NSUInteger indexOfMin = ...
NSUInteger indexOfMax = ...
Then in -dataLabelForPlot:recordIndex: :
-(CPTLayer *)dataLabelForPlot:(CPTPlot *)plot recordIndex:(NSUInteger)index {
if (plot.identifier == yourPlotID) // optional if you only have one plot
{
if (index == indexOfMin)
{
return [[CPTTextLayer alloc] initWithText:#"Your Min Label"];
}
else if (index == indexOfMax)
{
return [[CPTTextLayer alloc] initWithText:#"Your Max Label"];
}
}
return nil;
}

Adding “Around me”-like directional arrows to UITableView

I'm trying to develop an App with an "Around Me"-like feature of a location list with small directional arrows on the side.
Bearing and offset to the different locations hadn't been a problem thanks to Stackoverflow and compensating the compass-lag did well with following tutorial:
http://www.sundh.com/blog/2011/09/stabalize-compass-of-iphone-with-gyroscope/
All the stuff works fine with only one location in that UITableView.
But when there are more than one location, the arrows won't turn smooth and it feels like my iPhone isn't fast enough for calculating the stuff and turning these multiple arrows but I don't know how to do that better.
At the moment I'm trying this (without the locations specific directional offset):
I'm saving all the UIImageViews of all the cells in an array
when getting a new yaw value I loop through the array an actualize all the Images Rotation
if(motionManager.isDeviceMotionAvailable) {
// Listen to events from the motionManager
motionHandler = ^ (CMDeviceMotion *motion, NSError *error) {
CMAttitude *currentAttitude = motion.attitude;
float yawValue = currentAttitude.yaw; // Use the yaw value
// Yaw values are in radians (-180 - 180), here we convert to degrees
float yawDegrees = CC_RADIANS_TO_DEGREES(yawValue);
currentYaw = yawDegrees;
// We add new compass value together with new yaw value
yawDegrees = newCompassTarget + (yawDegrees - offsetG);
// Degrees should always be positive
if(yawDegrees < 0) {
yawDegrees = yawDegrees + 360;
}
compassDif.text = [NSString stringWithFormat:#"Gyro: %f",yawDegrees]; // Debug
float gyroDegrees = (yawDegrees*radianConst);
// If there is a new compass value the gyro graphic animates to this position
if(updateCompass) {
[self setRotateArrow:gyroDegrees animated:YES];
[self commitAnimations];
updateCompass = 0;
} else {
[self setRotateArrow:gyroDegrees animated:NO];
[UIView commitAnimations];
}
};
and the setRotateArrow:animated method:
- (void) setRotateArrow:(float)degrees animated:(BOOL)animated{
UIImage *arrowImage = [UIImage imageNamed:#"DirectionArrow.png"];
for (int i = 0; i<arrowImageViews.count; i++) {
[(UIImageView *)[arrowImageViews objectAtIndex:i] setImage:arrowImage];
CGFloat arrowTransform = degrees;
//Rotate the Arrow
CGAffineTransform rotate = CGAffineTransformMakeRotation(arrowTransform);
[(UIImageView *)[arrowImageViews objectAtIndex:i] setTransform:rotate];
}
}
If anyone got an idea how to get the arrows rotation following smoothly the device rotation I would be very thankful.

Creating Coreplot line graph with x and y axes in iOS

I have looked over a lot of sample tutorials of Core plot but having issues in most of them. If Can anyone provide a working tutorial for creating line graph with data X=(Sep,Oct,Nov,Dec) and Y=(20,40,80,30) with X & Y axes also using Core Plot framework in iOS? Any code would be much helpful to me..
If you want to make a linear graph in core plot, there are some things to keep in mind. First make sure that you make your view controller able to graph things. You need to make it the plot delegate, plot data source and the plotspace delegate.
#interface ViewController : UIViewController <CPTScatterPlotDelegate, CPTPlotSpaceDelegate, CPTPlotDataSource>
This is added in the .h file. **Don't forget to import the CorePlot-cocoaTouch.h too!
Next, in the view did appear method you would want to place your variables into an array. Here is a sample that I did to make a quick linear graph.
- (void)viewDidAppear:(BOOL)animated
{
float b = 1;
float c = 5;
Xmax = 10;
Xmin = -10;
Ymax = 10;
Ymin = -10;
float inc = (Xmax - Xmin) / 100.0f;
float l = Xmin;
NSMutableArray *linearstuff = [NSMutableArray array];
for (int i = 0; i < 100; i ++)
{
float y = (b * (l)) + c;
[linearstuff addObject:[NSValue valueWithCGPoint:CGPointMake(l, y)]];
NSLog(#"X and Y = %.2f, %.2f", l, y);
l = l + inc;
}
self.data = linearstuff;
[self initPlot];
}
The call to [self initPlot] calls a function to actually make the graphs. It is very similar to all the sample code that is out there.
Once you have your data into an array, the next things is to make the graphs the way you want them to look. Once again take a look at all the code for configureHost, configure Graph, and stuff like that, it is right on the Core Plot website. Another important thing to remember is the numberOfRecordsForPlot method. Here is sample of mine. This lets you know how many data points you have.
- (NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot
{
return [_data count];
}
_data is the array I made to store everything. Next you want to graph the data. With numberForPlot method. Once again here is a sample.
- (NSNumber *)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
{
NSLog(#"numberForPlot");
if ([plot.identifier isEqual:#"linear"])
{
NSValue *value = [self.data objectAtIndex:index];
CGPoint point = [value CGPointValue];
// FieldEnum determines if we return an X or Y value.
if (fieldEnum == CPTScatterPlotFieldX)
{
return [NSNumber numberWithFloat:point.x];
}
else // Y-Axis
{
return [NSNumber numberWithFloat:point.y];
}
NSLog(#"x is %.2f", point.x);
NSLog(#"y is %.2f", point.y);
}
return [NSNumber numberWithFloat:0];
}
Hopefully this will get you started. Core Plot is an excellent way to graph things and their website is full of great information. Hope this helps.

CorePlot (IOS): plotting the visible area (after zoom-in /zoom-out)

Problem:
Lets say I have a equation:
y = x^2
Using core-plot I doing this:
-(NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot {
return 3000;
}
-(NSNumber *)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
{
NSNumber *num = nil;
switch ( fieldEnum ) {
case CPTScatterPlotFieldX:
num = [NSNumber numberWithUnsignedInteger:index];
break;
case CPTScatterPlotFieldY:
num = [NSNumber numberWithUnsignedInteger:index*index];
break;
}
return num;
}
But this will draw only 3000 points, even after zoom-out (when X-axis shows points from 0 to 5000). So rest of the area undrawn.
What I want is: after zoom-out, I can refresh the plot in full visible area.
Anybody know how to do it?
First of all, I would limit the number of points to no more than the number of pixels available to draw the plot. Any more than that just takes more time to draw for no visible benefit. Look at the size of the plot area bounds to get the dimensions of the drawing area. Multiply the width and height by the contentsScale if you might be running on a device with a Retina display.
You can use a plot space delegate to find out when the user zooms or scrolls the graph. Call reloadData on the plot inside your delegate method to recalculate the plot data.

How to show label on Bar at bottom - Core Plot?

How to show a Label on Bar at Bottom?,
I am using below method and setting labeloffset, but it shows label on top
(In the image label text "Test" is displaying in the middle of bar, but i need to display bottom).
-(CPTLayer *)dataLabelForPlot:(CPTPlot *)plot recordIndex:(NSUInteger)index
{
static CPTMutableTextStyle *labelText = nil;
if ( !labelText ) {
labelText = [[CPTMutableTextStyle alloc] init];
labelText.color = [CPTColor redColor];
}
NSString *labelValue = #"Test";
NSDictionary *testsDict = [assmentsTestsForGraphArray objectAtIndex:index];
NSInteger score = [[testsDict valueForKey:#"Score"] integerValue];
CPTTextLayer *newLayer = [[CPTTextLayer alloc] initWithText:labelValue style:labelText];
plot.labelOffset = -score;//0;//-10;
return newLayer;
}
There are at least three different ways to do this:
Create a second plot (bar plot or scatter plot; it doesn't really matter) and give it the same x-values as the original plot but set all of the y-values equal to the vertical position of the x-axis. Label the second plot and not the first. Set up the second plot so it doesn't draw anything except the label, i.e., set all line styles and fills to nil.
Use custom axis labels on the x-axis rather than plot data labels.
Create plot space annotations for each label instead of plot data labels.
Sorry, I wasn't thinking right yesterday. The beginning of my first answer remains the same.
Bar plot's labels are (to the best of my very limited knowledge) set to the max value of a bar. So in order to get the label offset to the bottom of the graph, you need to to come up with an algorithm that works well for the graph you have. The best place to set the offset, in my opinion, is in the numberForPlot delegate function when the fieldEnum is CPTBarPlotFieldBarTip. I couldn't spend very much time figuring out the proper algorithm, but if you use the Plot_Gallery_iOS example project and edit VerticalBarChart.m, you'll find a wide range of offsets you have to use. Here's what little I did find though:
-(NSNumber *)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
{
NSNumber *num = nil;
if ( fieldEnum == CPTBarPlotFieldBarLocation ) {
// location
if ( [plot.identifier isEqual:#"Bar Plot 2"] ) {
num = [NSDecimalNumber numberWithInt:index];
}
else {
num = [NSDecimalNumber numberWithInt:index];
}
}
else if ( fieldEnum == CPTBarPlotFieldBarTip ) {
// length
if ( [plot.identifier isEqual:#"Bar Plot 2"] ) {
num = [NSDecimalNumber numberWithInt:index];
}
else {
num = [NSDecimalNumber numberWithInt:(index + 1) * (index + 1)];
plot.labelOffset = -1.0;
//-1.0 works well for the first bar, -345.0 works well for the last bar
}
}
else {
// base
if ( [plot.identifier isEqual:#"Bar Plot 2"] ) {
num = [NSDecimalNumber numberWithInt:0];
}
else {
num = [NSDecimalNumber numberWithInt:index];
}
}
return num;
}
-1.0 works well for the first bar (height of 1 unit), -345.0 works well for the last bar (height of 100 units). Sorry I led you in the wrong direction yesterday and that I don't have enough time to help you come up with a good algorithm, but hopefully that helps point you in the right direction.

Resources