CPTScatterPlotInterpolationCurved going out of bounds unable to determine scale - charts

I have determined somewhat flexible way to determine size of chart and axes, but I've got problem with CPTScatterPlotInterpolationCurved, because it can be higher than maximum data provided (unable to calculate how much easily).
Is there any property or method that can return maximum value after interpolation?
Example here (look at the top cut # green tick)
Code that calculates bounds:
// Setup scatter plot space
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)self.graph.defaultPlotSpace;
CGFloat margin = [[self.dataArray valueForKeyPath:#"#max.floatValue"] integerValue]*0.10;
double xLow = 0.0f;
double yHigh = 0;
double yLow = 0;
double xHigh = [self drawnDataTilPlot:self.graph.allPlots.lastObject] + [self.dividedDataArray.lastObject count]-1;
if (round([[self.dataArray valueForKeyPath:#"#max.floatValue"] integerValue]/10)*10 < [[self.dataArray valueForKeyPath:#"#max.floatValue"] integerValue]) {
yHigh = [[self.dataArray valueForKeyPath:#"#max.floatValue"] integerValue] + margin;
}
else {
yHigh = round([[self.dataArray valueForKeyPath:#"#max.floatValue"] integerValue]/10)*10+margin;
}
if (self.type == GRChartTypeScatterLine) {
yLow = [[self.dataArray valueForKeyPath:#"#min.floatValue"] integerValue];
yLow -= margin*0.5;
yHigh -= yLow;
}
plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(xLow) length:CPTDecimalFromFloat(xHigh)];
if ([[self.dataArray valueForKeyPath:#"#max.integerValue"]integerValue]==0 &&
[[self.dataArray valueForKeyPath:#"#min.integerValue"]integerValue]==0){
plotSpace.yRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(-1) length:CPTDecimalFromFloat(2)];
}
else {
plotSpace.yRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(yLow) length:CPTDecimalFromFloat(yHigh)];
}

There isn't any way to do this right now. See the comments on issue 131 for one possible workaround.

Related

Core plot create dynamic Y axis and display graph very clear

How can i create dynamic y axis by only passing yMin and yMax graph plot automatically and display very clear.
For Example my Data is for Y axis 99.7, 99.3, 99.2, 99.0 ,100 .
so i want to y axis min start from 99 to 100 with 0.1 increment by which graph will display very clear.
my code snippet is bellow
// 4 - Configre the y-axis
y_monthly.title = lbl_M_HasDelay.text;
y_monthly.labelAlignment = CPTAlignmentCenter;
y_monthly.titleTextStyle = axisTitleStyle;
y_monthly.titleOffset = -30.0f;
y_monthly.axisLineStyle = axisLineStyle;
y_monthly.majorGridLineStyle = gridLineStyle;
y_monthly.labelingPolicy = CPTAxisLabelingPolicyNone;
y_monthly.labelTextStyle = axisTextStyle;
y_monthly.labelOffset = 16.0f;
y_monthly.majorTickLineStyle = axisLineStyle;
y_monthly.majorTickLength = 4.0f;
y_monthly.minorTickLength = 2.0f;
y_monthly.tickDirection = CPTSignPositive;
NSMutableSet *yLabels = [NSMutableSet set];
NSMutableSet *yMajorLocations = [NSMutableSet set];
NSMutableSet *yMinorLocations = [NSMutableSet set];
float minorIncrement=month_MaxY1/10;
float majorIncrement = minorIncrement*2;
float yMax = month_MaxY1;
for (float j = minorIncrement; j <= yMax; j += minorIncrement)
{
float mod = fmodf(j, majorIncrement);
if (mod == 0) {
CPTAxisLabel *label = [[CPTAxisLabel alloc] initWithText:[NSString stringWithFormat:#""]textStyle:y_monthly.labelTextStyle];
[label setAlignment:CPTAlignmentTop];
NSDecimal location = CPTDecimalFromFloat(j);
label.tickLocation = location;
label.offset = -y_monthly.majorTickLength - y_monthly.labelOffset;
if (label) {
[yLabels addObject:label];
}
[yMajorLocations addObject:[NSDecimalNumber decimalNumberWithDecimal:location]];
} else {
[yMinorLocations addObject:[NSDecimalNumber decimalNumberWithDecimal:CPTDecimalFromFloat(j)]];
}
}
y_monthly.axisLabels = yLabels;
y_monthly.majorTickLocations = yMajorLocations;
y_monthly.minorTickLocations = yMinorLocations;
where month_MaxY1 =100 , and month_MinY1 =90.0.
Please help me
Set the range of the y-axis using the plot space:
plotSpace.yRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromDouble(99.0)
length:CPTDecimalFromDouble(1.0)];
Label the axis:
y_monthly.labelingPolicy = CPTAxisLabelingPolicyFixedInterval;
y_monthly.majorIntervalLength = CPTDecimalFromDouble(0.1);
This will make ticks and labels every 0.1 unit along the y-axis. Set the labelFormatter and labelTextStyle to control the appearance of the labels.
I guess your problem is to make your X axis to be displayed at the yMin position of your graph.
What you need to do is:
x_axis.orthogonalCoordinateDecimal = CPTDecimalFromDouble(yMin);

Core Plot on iPhone - Extra white Lines beside Axes

I'm using Core Plot to do some graphing in an iOS app. I'm in the midst of configuring the axes, and I'm getting strange white lines below the X and to the left of the Y axis, as seen in the following picture:
My set-up code for the graph is as follows:
// 1 - Create the graph
CPTGraph *graph = [[CPTXYGraph alloc] initWithFrame:self.barHostView.bounds];
graph.plotAreaFrame.masksToBorder = NO;
self.barHostView.hostedGraph = graph;
// 2 - Configure the graph
[graph applyTheme:[CPTTheme themeNamed:kCPTPlainBlackTheme]];
graph.paddingBottom = 30.0f;
graph.paddingLeft = 30.0f;
graph.paddingTop = -1.0f;
graph.paddingRight = -5.0f;
// 3 - Set up styles
CPTMutableTextStyle *titleStyle = [CPTMutableTextStyle textStyle];
titleStyle.color = [CPTColor whiteColor];
titleStyle.fontName = #"Helvetica-Bold";
titleStyle.fontSize = 16.0f;
// 4 - Set up title
graph.title = [NSString stringWithFormat:#"Usage on %#", self.turnout];
graph.titleTextStyle = titleStyle;
graph.titlePlotAreaFrameAnchor = CPTRectAnchorTop;
graph.titleDisplacement = CGPointMake(0.0f, -16.0f);
// 5 - Set up plot space
CGFloat xMin = -1.5f;
CGFloat xMax = [self.points count] + 2;
CGFloat yMin = 0.0f;
self.yMax = [[self greatestFlowValue] floatValue] * 1.4;
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *) graph.defaultPlotSpace;
plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(xMin) length:CPTDecimalFromFloat(xMax)];
plotSpace.yRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(yMin) length:CPTDecimalFromFloat(self.yMax)];
CPTMutablePlotRange *yRange = [plotSpace.yRange mutableCopy];
[yRange expandRangeByFactor:CPTDecimalFromCGFloat(1.65f)];
plotSpace.yRange = yRange;
I more ore less copied this out of a tutorial and then modified it. In the walk-through, they didn't have labels on the axes -- which they had set at the left and bottom of the screen.
Can anyone explain what is causing these extra lines and how I might disable them? They only appear on my Bar Charts... I have other graphs that use scatter plots without any similar results.
It looks like you have a border on you plotAreaFrame(set by the theme), try removing it after applying the theme:
graph.plotAreaFrame.borderLineStyle = nil;

CoreText,Copy&Paste

I want use UIMenuController in CoreText,but I can't select the words,I think I must compute the coordinate of the words,but the coordinate can't be correspondence with NSRange.Is there any function to solve it?
Sorry my English is not good~
Here is my code
CFArrayRef lines = CTFrameGetLines(leftFrame);
CFIndex i, total = CFArrayGetCount(lines);
CGFloat y;
for (i = 0; i < total; i++) {
CGPoint origins;
CTFrameGetLineOrigins( leftFrame, CFRangeMake(i, 1), &origins);
CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(lines, i);
y = self.bounds.origin.y + self.bounds.size.height - origins.y;
//CTLineDraw(line, UIGraphicsGetCurrentContext());
CFArrayRef runs = CTLineGetGlyphRuns(line);
CFIndex r, runsTotal = CFArrayGetCount(runs);
//NSLog(#"runsTotal = %d",runsTotal);
for (r = 0; r < runsTotal; r++) {
CGRect runBounds = CTRunGetImageBounds(CFArrayGetValueAtIndex(runs, r), context, CFRangeMake(0, 0));
NSLog(#"runBounds.x = %f,runBounds.y = %f",runBounds.origin.x,runBounds.origin.y);
CFIndex index = CTRunGetStringRange(CFArrayGetValueAtIndex(runs, r)).location;
//NSLog(#"%d",index);
}
}
You can use a tap gesture to get the tap position,
and you have the CTFrame,
Then You can have the needed text frame calculated.
Here is an example , how to calculate two character's frame of the tap position .
pass in CTFrame and Tap Point, get two character's frame and its range in the CTFrame
+(CGRect)parserRectWithPoint:(CGPoint)point range:(NSRange *)selectRange frameRef:(CTFrameRef)frameRef
{
CFIndex index = -1;
CGPathRef pathRef = CTFrameGetPath(frameRef);
CGRect bounds = CGPathGetBoundingBox(pathRef);
CGRect rect = CGRectZero;
// get lines
NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frameRef);
if (!lines) {
return rect;
}
NSInteger lineCount = [lines count];
// prepare for origins of each line
CGPoint *origins = malloc(lineCount * sizeof(CGPoint));
if (lineCount) {
CTFrameGetLineOrigins(frameRef, CFRangeMake(0, 0), origins);
// check tap point in every line
// found, then break
for (int i = 0; i<lineCount; i++) {
CGPoint baselineOrigin = origins[i];
CTLineRef line = (__bridge CTLineRef)[lines objectAtIndex:i];
CGFloat ascent,descent,linegap; //声明字体的上行高度和下行高度和行距
CGFloat lineWidth = CTLineGetTypographicBounds(line, &ascent, &descent, &linegap);
CGRect lineFrame = CGRectMake(baselineOrigin.x, CGRectGetHeight(bounds)-baselineOrigin.y-ascent, lineWidth, ascent+descent+linegap+[LSYReadConfig shareInstance].lineSpace);
if (CGRectContainsPoint(lineFrame,point)){
// line found
CFRange stringRange = CTLineGetStringRange(line);
// glyph found
index = CTLineGetStringIndexForPosition(line, point);
CGFloat xStart = CTLineGetOffsetForStringIndex(line, index, NULL);
CGFloat xEnd;
// choose two words by default
if (index > stringRange.location+stringRange.length-2) {
xEnd = xStart;
xStart = CTLineGetOffsetForStringIndex(line,index-2,NULL);
(*selectRange).location = index-2;
}
else{
xEnd = CTLineGetOffsetForStringIndex(line,index+2,NULL);
(*selectRange).location = index;
}
// choose two character
(*selectRange).length = 2;
rect = CGRectMake(origins[i].x+xStart,baselineOrigin.y-descent,fabs(xStart-xEnd), ascent+descent);
break;
}
}
}
free(origins);
return rect;
}
core-text does not support the text-selection, you need to do it by yourself, please follow the EGOTextView as example

How to implement Zoom-in and Zoom-Out with Core-Plot line chart on iPhone?

Is it possible to implement Zoom-in and Zoom-Out with Core-Plot Chart component on iPhone? If so please suggest me how to do it?
Thanks in advance.
Yes, The way I do it is by adjusting the PlotSpace like so (probably not the best solution, but the best one i could come up with):
-(void)zoomOut
{
CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)graph.defaultPlotSpace;
graphScaleX+=5;
graphScaleY+=5;
plotSpace.xRange = [CPPlotRange plotRangeWithLocation:plotSpace.xRange.location length:CPDecimalFromFloat(graphScaleX)];
plotSpace.yRange = [CPPlotRange plotRangeWithLocation:plotSpace.yRange.location length:CPDecimalFromFloat(graphScaleY)];
}
-(void)zoomIn
{
CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)graph.defaultPlotSpace;
graphScaleX-=5;
graphScaleY-=5;
plotSpace.xRange = [CPPlotRange plotRangeWithLocation:plotSpace.xRange.location length:CPDecimalFromFloat(graphScaleX)];
plotSpace.yRange = [CPPlotRange plotRangeWithLocation:plotSpace.yRange.location length:CPDecimalFromFloat(graphScaleY)];
}
You can set plotSpace delegate to some class instance that conforms to CPTPlotSpaceDelegate protocol(for example self)[plotSpace setDelegate:self];.
configure the plotSpace so it interacts with user [plotSpace setAllowsUserInteraction:YES];
And then in that class implement this method -(BOOL)plotSpace:(CPTPlotSpace *)space shouldScaleBy:(CGFloat)interactionScale aboutPoint:(CGPoint)interactionPoint. Return YES if you want the corePlot to resize your graph automatically. Or NO and do something like in previous answer.
Note: if you want your current controller class (self) to be a delegate, be sure that your interface looks like this #interface CurrentController : UISomeController<...,CPTPlotSpaceDelegate>{}. So now it conforms to CPTPlotSpaceDelegate protocol.
Using the default user interaction was not enough for me because:
It scales both X & Y axis simultaneously and I only wanted to scale
X axis
Pan does not allow you go past zero and it jumps and
basically doesn't work right..
My solution was to do it myself
I added 2 UIGestureRecognizer for Pan, Zoom
UIPanGestureRecognizer* pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panChart:)];
[pan setMinimumNumberOfTouches:1];
[pan setMaximumNumberOfTouches:1];
[self.view addGestureRecognizer:pan];
UIPinchGestureRecognizer* scale = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(scaleChart:)];
[self.view addGestureRecognizer:scale];
I then implemented the code to do what I needed
Pan Chart
- (void)panChart:(UIPinchGestureRecognizer *)gestureRecognizer {
CPTGraph *theGraph = [graphs objectAtIndex:0];
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)theGraph.defaultPlotSpace;
CGPoint point = [gestureRecognizer locationInView:self.view];
if([(UIPanGestureRecognizer*)gestureRecognizer state] == UIGestureRecognizerStateBegan) {
firstX = point.x;
firstY = point.y;
originX = chartBounds.origin.x;
originY = chartBounds.origin.y;
//NSLog(#"first (%f,%f)", firstX, firstY);
}
CGRect frame = theGraph.frame;
float xMove = ((firstX-point.x)/(frame.size.width/chartBounds.size.width));
float yMove = ((firstY-point.y)/(frame.size.height/chartBounds.size.height));
if(firstX-point.x == 0){
xMove = 0;
}
if(firstY-point.y == 0){
yMove = 0;
}
//NSLog(#"frame: (%f, %f)",frame.size.width, frame.size.height);
//NSLog(#"touch (%f,%f)",firstX-point.x,firstY-point.y);
//NSLog(#"move (%f,%f)",xMove,yMove);
chartBounds.origin.x = originX+xMove;
chartBounds.origin.y = originY-yMove;
plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(chartBounds.origin.x) length:CPTDecimalFromFloat(chartBounds.size.width)];
plotSpace.yRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(chartBounds.origin.y) length:CPTDecimalFromFloat(chartBounds.size.height)];
}
Scale Chart
- (void)scaleChart:(UIPinchGestureRecognizer *)gestureRecognizer {
if (kMaxDataPoints < kDataPointsHistory) {
if ([gestureRecognizer scale] < 1) {
if(kMaxDataPoints/10 < 1){
kMaxDataPoints += 1;
}else{
kMaxDataPoints += kMaxDataPoints/10;
}
}
}
if (kMaxDataPoints > 5) {
if ([gestureRecognizer scale] > 1){
if(kMaxDataPoints/10 < 1){
kMaxDataPoints -= 1;
}else{
kMaxDataPoints -= kMaxDataPoints/10;
}
}
}
if(kMaxDataPoints <= 5){
kMaxDataPoints = 5;
}
if(kMaxDataPoints > kDataPointsHistory){
kMaxDataPoints = kDataPointsHistory;
}
chartBounds.size.width = kMaxDataPoints;
CPTGraph *theGraph = [graphs objectAtIndex:0];
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)theGraph.defaultPlotSpace;
plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(chartBounds.origin.x) length:CPTDecimalFromFloat(chartBounds.size.width)];
//Need to calculate if its horizontal or vertical pinch gesture (not doing that yet :) )
//plotSpace.yRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(chartBounds.origin.y) length:CPTDecimalFromFloat(chartBounds.size.height)];
}
To scale only in x axis just force the new y-scale to be the old one in the delegate:
-(BOOL)plotSpace:(CPTPlotSpace *)space shouldScaleBy:(CGFloat)interactionScale aboutPoint:(CGPoint)interactionPoint;

Why won't my x-axis show with core-plot on the iPhone?

EDIT: I think my question is better phrased as: How can I have a Y-axis that doesn't start at zero? It seems like the x-axis always gets placed at the y=0, but I would like the x-axis to be at some positive number on the y-axis.
Here's a graph with more regular data... I just wish the x-axis was placed at the minimum y-value for the plot (about 77), instead of at 0.
Here is the function I use to create the graph.
It's a whole bunch of code, and I'm not quite sure which piece might be off to not display the x-axis.
Could the x-axis not showing have something to do with my data?
- (void) showGraph:(SavedDetailScreen*)dataSource {
// create the graph and add it to the view
CPXYGraph *graph = [[CPXYGraph alloc] initWithFrame: CGRectZero];
graph.plotArea.masksToBorder = NO;
CPLayerHostingView *graphView = [[CPLayerHostingView alloc] initWithFrame:CGRectMake(0,0, 280, 240)];
[self addSubview:graphView];
graphView.hostedLayer = graph;
graph.paddingLeft = 50.0;
graph.paddingTop = 20.0;
graph.paddingRight = 10.0;
graph.paddingBottom = 40.0;
// set up the ranges for the graph axis
float minElevation = dataSource.track.tbStats.minAlt;
float maxElevation = dataSource.track.tbStats.maxAlt-dataSource.track.tbStats.minAlt;
float minDistance = 0.0f;
float maxDistance = dataSource.track.tbStats.totalDistance;
CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)graph.defaultPlotSpace;
plotSpace.xRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(minDistance)
length:CPDecimalFromFloat(maxDistance)];
plotSpace.yRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(minElevation)
length:CPDecimalFromFloat(maxElevation)];
// style the graph with white text and lines
CPTextStyle *whiteText = [CPTextStyle textStyle];
whiteText.color = [CPColor whiteColor];
CPLineStyle *whiteStyle = [CPLineStyle lineStyle];
whiteStyle.lineColor = [CPColor whiteColor];
whiteStyle.lineWidth = 2.0f;
// set up the axis
CPXYAxisSet *axisSet = (CPXYAxisSet *)graph.axisSet;
CPXYAxis *x = axisSet.xAxis;
CPXYAxis *y = axisSet.yAxis;
x.majorIntervalLength = CPDecimalFromFloat(maxDistance/10.0f);
x.minorTicksPerInterval = 0;
x.majorTickLineStyle = whiteStyle;
x.minorTickLineStyle = whiteStyle;
x.axisLineStyle = whiteStyle;
x.minorTickLength = 5.0f;
x.majorTickLength = 10.0f;
x.labelOffset = 3.0f;
x.labelTextStyle = whiteText;
y.majorIntervalLength = CPDecimalFromFloat(maxElevation/5.0f);
y.minorTicksPerInterval = 0;
y.majorTickLineStyle = whiteStyle;
y.minorTickLineStyle = whiteStyle;
y.axisLineStyle = whiteStyle;
y.minorTickLength = 5.0f;
y.majorTickLength = 10.0f;
y.labelOffset = 3.0f;
y.labelTextStyle = whiteText;
CPScatterPlot *plot = [[[CPScatterPlot alloc] initWithFrame:graph.bounds] autorelease];
plot.dataLineStyle.lineWidth = 2.0f;
plot.dataLineStyle.lineColor = [CPColor blueColor];
plot.dataSource = dataSource;
[graph addPlot:plot];
CPPlotSymbol *greenCirclePlotSymbol = [CPPlotSymbol ellipsePlotSymbol];
greenCirclePlotSymbol.fill = [CPFill fillWithColor:[CPColor greenColor]];
greenCirclePlotSymbol.size = CGSizeMake(2.0, 2.0);
plot.plotSymbol = greenCirclePlotSymbol;
}
I've had the same problem yesterday/today and the solution seems to be using the orthogonalCoordinateDecimal property on the axis. For example:
graph.axisSet.xAxis.orthogonalCoordinateDecimal = CPDecimalFromFloat(minHeight);
You set the position of an axis on the orthogonal axis using the constantCoordinateValue property of the axis. Just set that property on the x axis to a positive value of y, and the axis should move up.
https://github.com/djw/core-plot/tree/9282845bddbb8c40ff314bbfa158beff797c91f7/examples
This states that the isFloatingAxis property has been removed from at least version 0.9.
I also found this in the discussion group as well http://code.google.com/p/core-plot/issues/detail?id=125
After some hunting, it looks like the new way to float an axis looks like this:
x.axisConstraints = [CPTConstraints constraintWithUpperOffset:132];
this framework really needs a better way to configure itself. I don't see any problem jumping out. Maybe just try simplifying the method to the point where debugging becomes manageable. Check the data that you are passing in to make sure it is what you expect it to be. Better yet, create some dummy data that you know will produce the plot you expect.