Core Plot: Show yearly labels on the x
For the plot values in my Core Plot chart I am displaying NSDate
based values on the x
-axis. The values encompass many years and are plotted on a daily basis.
I would like to display labels on the x
-axis for each year or month, but not for each day. This is why I can't use the labeling method shown in the Core Plot example app DatePlot
.
Currently I am creating the labels manually (as shown in the following code snippet). Unfortunately, this solution leads to the issue shown in the screen shot below (the label for the year 2001 is too close to 2002).
[sortedArray enumerateObjectsUsingBlock:
^(Price *priceItem, NSUInteger idx, BOOL *stop) {
NSDateComponents *yearComponents =
[gregorian components:NSYearCalendarUnit
fromDate:price.dateTime];
NSDateComponents *monthComponents =
[gregorian components:NSMonthCalendarUnit
fromDate:price.dateTime];
NSInteger year = [yearComponents year];
NSInteger month = [monthComponents month];
if (month != previousMonth) {
[minorTickLocations addObject:
[NSDecimalNumber numberWithUnsignedInteger:idx]];
previousMonth = month;
}
if (year != previousYear) {
CPTAxisLabel *newLabel = [[CPTAxisLabel alloc]
initWithText: [NSString stringWithFormat:@"%u", year]
textStyle: axisTitleTextStyle];
newLabel.tickLocation = CPTDecimalFromUnsignedInteger(idx);
newLabel.offset = x.labelOffset + x.majorTickLength / 2.0;
[xAxisLabels addObject:newLabel];
[majorTickLocations addObject:
[NSDecimalNumber numberWithUnsignedInteger:idx]];
previousYear = year;
}
}];
I am wondering if there is a better way to code this using standard Core Plot methods.
It would be great if the date formatter behaviour could be used to show only years ( YYYY
), month and years ( MMM-YYYY
) or only months ( MMM
).
Edit
As Eric recommended in his post, I am converting my dates to fractions of year in order to be able to use CPTDateFormatter
. This is how I set up the chart (Initially, I just want to plot a tick per calendar year):
x.labelingPolicy = CPTAxisLabelingPolicyFixedInterval;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy"];
CPTCalendarFormatter *calendarFormatter = [[CPTCalendarFormatter alloc]
initWithDateFormatter:dateFormatter];
calendarFormatter.referenceDate = [sortedPrices[0]
valueForKey:@"dateTime"];
calendarFormatter.referenceCalendarUnit = NSYearCalendarUnit;
x.labelFormatter = calendarFormatter;
This is how I convert my initial integer based index to a "fraction-of-year" based index:
- (NSArray*)indexArrayOfDatesArray:(NSArray*)dates
{
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *startDate = [sortedCommonSharePrices[0] valueForKey:@"dateTime"];
NSDateComponents *yearComponents = [gregorian components: NSYearCalendarUnit
fromDate:startDate];
NSUInteger startYear = yearComponents.year;
NSMutableArray *array = [NSMutableArray arrayWithCapacity:dates.count];
[dates enumerateObjectsUsingBlock:^(NSDate *date, NSUInteger idx, BOOL *stop) {
NSDateComponents *yearComponents = [gregorian
components:NSYearCalendarUnit fromDate:date];
NSInteger years = [yearComponents year];
NSUInteger numberOfDaysSinceStartOfYear =
[gregorian ordinalityOfUnit:NSDayCalendarUnit
inUnit:NSYearCalendarUnit forDate:date];
NSUInteger totalNumberOfDaysInYear = 365;
double fractionOfYear = (double)numberOfDaysSinceStartOfYear /
(double)totalNumberOfDaysInYear + (double)years - (double)startYear;
[array addObject:[NSNumber numberWithDouble:fractionOfYear]];
}];
return array;
}
This seems to work ok, but seems to be a lot of code (any thoughts on improving the code ?).
Question
When zooming and panning (using plotSpace:willChangePlotRangeTo
) I want to set the x-axis labels to the new date range (based on the number of days in the new plot range. However, I get an incorrect number of ticks and wrong labels (please refer to the screenshot below).
How would I need to set the labeling parameters to get the labeling right?
Thank you for help!
Code snippet from plotSpace:willChangePlotRangeTo
:
NSDateComponents *components = [gregorian components:NSDayCalendarUnit
fromDate:newStartDate toDate:newEndDate options:0];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
CPTCalendarFormatter *calendarFormatter =
[[CPTCalendarFormatter alloc] initWithDateFormatter:dateFormatter];
calendarFormatter.referenceDate = [sortedPrices[0] valueForKey:@"dateTime"];
if (components.day < 365) {
dateFormatter.dateFormat = @"ddd-MMM-yyyy";
calendarFormatter.referenceCalendarUnit = NSDayCalendarUnit;
} else if (components.day < 365 * 2) {
dateFormatter.dateFormat = @"MMM yyyy";
calendarFormatter.referenceCalendarUnit = NSYearCalendarUnit;
} else if (components.day < 365 * 4) {
dateFormatter.dateFormat = @"qqq yyyy";
calendarFormatter.referenceCalendarUnit = NSYearCalendarUnit;
} else {
dateFormatter.dateFormat = @"yyyy";
calendarFormatter.referenceCalendarUnit = NSYearCalendarUnit;
}
CPTXYAxis *x = axisSet.xAxis;
x.preferredNumberOfMajorTicks = 10;
x.majorIntervalLength = CPTDecimalFromDouble(0.1);
x.minorTicksPerInterval = 1;
x.labelFormatter = calendarFormatter;
Provide your own labeling policy, like this:
...
CPTXYAxis *x = axisSet.xAxis;
int totalNumberOfYearsToShow = 3;
int startYear = 2010;
int i = 0;
NSMutableSet *xLabels = [NSMutableSet setWithCapacity:totalRecords];
NSMutableSet *xLocations = [NSMutableSet setWithCapacity:totalRecords];
while (i < totalNumberOfYearsToShow)
{
NSNumber *xData = [NSNumber numberWithInt:(i + startYear)];
CPTAxisLabel *label = [[CPTAxisLabel alloc] initWithText:[xData stringValue] textStyle:x.labelTextStyle];
label.tickLocation = CPTDecimalFromInt([xData intValue]);
label.offset = x.majorTickLength;
if (label)
{
[xLabels addObject:label];
[xLocations addObject:xData];
}
i ++;
}
x.labelingPolicy = CPTAxisLabelingPolicyNone;
x.axisLabels = xLabels;
x.majorTickLocations = xLocations;
...
Hope that helps.
I can't use the labeling method shown in the Core Plot example app DatePlot.
Why not? With the fixed interval labeling policy, just set the majorIntervalLength
to the equivalent of one year and use a CPTCalendarFormatter
or CPTTimeFormatter
to format the labels.
上一篇: 日期轴用OpenXML作为数值轴
下一篇: 核心图:在x上显示年度标签