Maggie
Maggie

Reputation: 8081

iPhone CorePlot pie chart get slices coordinates

I'm using CorePlot to draw PieChart. I would like to display labels for slices on slices themselves. Is there a way to get coordinates of each slice and then setting the frame of CPTLayer that holds the text label to adjust to coordinates of the slice?

What I am doing so far:

-(CPRLayer*) datLabelForPlot(CPTPlot*)plot recordIndex:(NSUInteger)index {
  static CPTMutableTextStyle *textStyle = nil;
  NSString *string = @"Test";

    if ( !textStyle) {
        textStyle= [[CPTMutableTextStyle alloc] init];
        textStyle.color = [CPTColor whiteColor];
    }
    CPTLayer *layer = [[[CPTLayer alloc] initWithFrame:CGRectMake(50,50, 100, 20)]autorelease];
    CPTTextLayer *newLayer = nil;
    newLayer = [[[CPTTextLayer alloc] initWithText:string style:textStyle] autorelease];
    [layer addSublayer:newLayer];
    return layer;
}

but regardless of the layer frame, label is always displayed at the same position (outside the chart). How to set the appropriate layer frame to display the text on the slice itself?

Here is the image of the points I would like to know: piechart

Upvotes: 0

Views: 2326

Answers (2)

Eric Skroch
Eric Skroch

Reputation: 27381

The centerAnchor is expressed as a fraction of the size of the plot area, so you can use the following code to compute its pixel position (point "O" on your picture):

CPTPieChart *pieChart; // the pie chart
CGRect plotAreaBounds = pieChart.plotArea.bounds;
CGPoint anchor = pieChart.centerAnchor;
CGPoint centerPoint = CGPointMake(plotAreaBounds.origin.x + plotAreaBounds.size.width * anchor.x,
                                  plotAreaBounds.origin.y + plotAreaBounds.size.height * anchor.y);

You can look at the Core Plot source code to see how the pie chart computes the positions of the labels. This code accounts for slices that are "exploded" and centers the label between what you called points "A" and "B", offset by the labelOffset. It hides the label if the datasource returned a missing value (NAN) for the slice. The index corresponds to the datasource index of the pie slice. The relevant bits are:

double currentWidth = [self cachedDoubleForField:CPTPieChartFieldSliceWidthNormalized recordIndex:index];
if ( isnan(currentWidth) ) {
    contentLayer.hidden = YES;
}
else {
    id<CPTPieChartDataSource> theDataSource = id<CPTPieChartDataSource>)self.dataSource;
    BOOL dataSourceProvidesRadialOffsets = [theDataSource respondsToSelector:@selector(radialOffsetForPieChart:recordIndex:)];
    CGFloat radialOffset = 0.0;
    if ( dataSourceProvidesRadialOffsets ) {
        radialOffset = [theDataSource radialOffsetForPieChart:self recordIndex:index];
    }

    CGFloat labelRadius = self.pieRadius + self.labelOffset + radialOffset;

    double startingWidth = 0.0;
    if ( index > 0 ) {
        startingWidth = [self cachedDoubleForField:CPTPieChartFieldSliceWidthSum recordIndex:index - 1];
    }
    CGFloat labelAngle = [self radiansForPieSliceValue:startingWidth + currentWidth / (CGFloat)2.0];

    label.displacement = CGPointMake( labelRadius * cos(labelAngle), labelRadius * sin(labelAngle) );
    contentLayer.hidden = NO;
}

Upvotes: 0

elibud
elibud

Reputation: 8169

Have you tried setting the CPPieChart labelOffset property to a negative value? May be it doesn't provide the level of precision that you need, but it's an easy solution.

Positive Offset:

Positive Offset

Negative Offset:

enter image description here

Upvotes: 3

Related Questions