AAChartModel / AAChartKit

📈📊🚀🚀🚀An elegant modern declarative data visualization chart framework for iOS, iPadOS and macOS. Extremely powerful, supports line, spline, area, areaspline, column, bar, pie, scatter, angular gauges, arearange, areasplinerange, columnrange, bubble, box plot, error bars, funnel, waterfall and polar chart types. 极其精美而又强大的现代化声明式数据可视化图表框架,支持柱状图、条形图、折线图、曲线图、折线填充图、曲线填充图、气泡图、扇形图、环形图、散点图、雷达图、混合图等各种类型的多达几十种的信息图图表,完全满足工作所需.
https://cocoapods.org/pods/AAChartKit
MIT License
4.71k stars 750 forks source link

折线图,在数据量非常多的情况下,会出现丢失 #1419

Closed solanderLK closed 1 year ago

solanderLK commented 1 year ago

问题描述: 折线图 ,展示5条数据,如图1,这是1个小时的数据图 ,iOS开发。系统12 .0~16.0 都试过。

图1

图2,10个小时数据图

图2

图3,20个小时以上数据tu图

截屏2022-11-02 15 51 17

从图1至图3 ,可以发现,随着时间和数据量的递增,温度4(黄色的线条) 消失了,我也做了缩小和放大,也找不到该线条; 数据量比对: 车速的数据量是 1540条,温度4的数据量是 21条,其他温度数据量大概也在1000条左右。 不知道是不是在数据量上面做了处理?数据量少的点就不显示了?还是因为屏幕像素点只能展示那么多的点,从而没有画出来? 希望可以解答一下。 PS: 在数据少量的情况下,是可以展示所有数据的。

AAChartModel commented 1 year ago

我在 demo 中测试如下:

配置 AAChartModel

- (NSArray *)generateRandomNumberArrayWithLength:(NSUInteger)length
                                     randomRange:(NSUInteger)randomRange
                                          minNum:(NSUInteger)minNum {
    NSMutableArray *randomNumArrA = [NSMutableArray array];
    for (int x = 0; x < length; x++) {
        int randomNum = arc4random() % randomRange + minNum;
        [randomNumArrA addObject:@(randomNum)];
    }
    return randomNumArrA;
}

//https://github.com/AAChartModel/AAChartKit/issues/1419
- (AAChartModel *)lineChartsWithLargeDifferencesInTheNumberOfDataInDifferentSeriesElement {
    return AAChartModel.new
        .chartTypeSet(AAChartTypeLine)
        .backgroundColorSet(AAColor.blackColor)
        .colorsThemeSet(@[@"#1e90ff",@"#04d69f",@"#ef476f",@"#ffd066",])
        .dataLabelsEnabledSet(false)
        .markerRadiusSet(@0)
        .seriesSet(@[
            AASeriesElement.new
                .nameSet(@"2017")
                .lineWidthSet(@6)
                .dataSet([self generateRandomNumberArrayWithLength:21 randomRange:5 minNum:100]),
            AASeriesElement.new
                .nameSet(@"2018")
                .lineWidthSet(@6)
                .dataSet([self generateRandomNumberArrayWithLength:1550 randomRange:100 minNum:200]),
            AASeriesElement.new
                .nameSet(@"2019")
                .lineWidthSet(@6)
                .dataSet([self generateRandomNumberArrayWithLength:1550 randomRange:150 minNum:400]),
            AASeriesElement.new
                .nameSet(@"2020")
                .lineWidthSet(@6)
                .dataSet([self generateRandomNumberArrayWithLength:1550 randomRange:150 minNum:600]),
        ]);
}

最终图表

IMG_1144

IMG_1145

从中可以看到, 虽然不同 AASeriesElement 的 data 数组元素个数有很大差异 (从21~1550之间), 最小data数组的的 AASeriesElement 在图表中仍然能够看到.

所以我暂未复现出你的问题.

AAChartModel commented 1 year ago

我也做了缩小和放大,也找不到该线条;

你可以试试点击图表的图例(AALegend), 让图表中只剩下数据最少的那个 AASeriesElement, 看看能否正常显示.

AAChartModel commented 1 year ago

不知道是不是在数据量上面做了处理?数据量少的点就不显示了?还是因为屏幕像素点只能展示那么多的点,从而没有画出来?

从我上面的测试示例中来看, 如果 data 的元素个数差异在21~1550之间, 应该还是能够显示所有 AASeriesElement 的.

solanderLK commented 1 year ago

我可以提供一份模拟数据,可以复现该问题。数据在 testData.md中(使用时,将后缀改为.json); testData.md 另外,我所使用的代码如下:

- (void)setUpChartView {
    [self configJsonData];
    CGFloat chartViewWidth  = self.view.frame.size.width;
    self.aaChartView = [[AAChartView alloc]initWithFrame:CGRectMake(0, 80, chartViewWidth, 300)];
    self.aaChartView.scrollEnabled = NO;
    self.aaChartView.backgroundColor = [UIColor whiteColor];
    [self.view addSubview:self.aaChartView];
    AAOptions *newOptions = [self configureTempDataChart];
    [self.aaChartView aa_drawChartWithOptions:newOptions];
}
- (void)configJsonData {
    NSString *path = [[NSBundle mainBundle] pathForResource:@"testData" ofType:@"json"];
    NSData *data = [[NSData alloc] initWithContentsOfFile:path];
    NSArray *dataArr = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
    for (NSDictionary *itemDic in dataArr) {
        NSString *time = [self getTimeStyleStringWithTimeString:[NSString stringWithFormat:@"%@",itemDic[@"time"]] withDateFormat:@"mm:ss"];
        [self.timeArr addObject:time];
        NSString *temp1 = [NSString stringWithFormat:@"%@",itemDic[@"temp1"]];
        if(temp1.length == 0){
            [self.temp1Arr addObject:@""];
        }else{
            [self.temp1Arr addObject:[NSNumber numberWithFloat:temp1.floatValue]];
        }
        NSString *temp2 = [NSString stringWithFormat:@"%@",itemDic[@"temp2"]];
        if(temp2.length == 0){
            [self.temp2Arr addObject:@""];
        }else{
            [self.temp2Arr addObject:[NSNumber numberWithFloat:temp2.floatValue]];
        }
        NSString *temp3 = [NSString stringWithFormat:@"%@",itemDic[@"temp3"]];
        if(temp3.length == 0){
            [self.temp3Arr addObject:@""];
        }else{
            [self.temp3Arr addObject:[NSNumber numberWithFloat:temp3.floatValue]];
        }
        NSString *temp4 = [NSString stringWithFormat:@"%@",itemDic[@"temp4"]];
        if(temp4.length == 0){
            [self.temp4Arr addObject:@""];
        }else{
            [self.temp4Arr addObject:[NSNumber numberWithFloat:temp4.floatValue]];
        }
        NSString *speed = [NSString stringWithFormat:@"%@",itemDic[@"speed"]];
        if(speed.length == 0){
            [self.speedArr addObject:@""];
        }else{
            [self.speedArr addObject:[NSNumber numberWithFloat:speed.floatValue]];
        }
    }
}
- (NSString *)getTimeStyleStringWithTimeString:(NSString *)timeString withDateFormat:(NSString *)replaceDateFormat{
    NSDateFormatter* dateFormat = [[NSDateFormatter alloc] init];
    [dateFormat setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSString *dateString=[NSString stringWithFormat:@"%@",timeString];
    [dateFormat setTimeZone:[NSTimeZone timeZoneWithName:@"Asia/Shanghai"]];
    NSDate *date =[dateFormat dateFromString:dateString];
    NSDateFormatter* dateFormat2 = [[NSDateFormatter alloc] init];
    [dateFormat2 setDateFormat:replaceDateFormat];
    NSString *publishtimeStr = [dateFormat2 stringFromDate:date];
    return publishtimeStr;
}

- (AAOptions *)configureTempDataChart{
    AAChart *aaChart = AAChart.new
    .typeSet(AAChartTypeLine)
    .backgroundColorSet(@"#FFFFFF");
    AATitle *aaTitle = AATitle.new
    .textSet(@"");
    AALabels *aaLables = AALabels.new
    .enabledSet(true)
    .styleSet(AAStyle.new
              .colorSet(@"#223A63"));
    AAXAxis *aaXAxis = AAXAxis.new
    .visibleSet(true)
    .labelsSet(aaLables)
    .categoriesSet([self.timeArr copy]);
    AAStyle *tempYAxisTitleStyle = AAStyle.new
    .colorSet(@"#FF7D00")
    .fontSizeSet(@"14px")
    .fontWeightSet(AAChartFontWeightTypeBold)
    .textOutlineSet(@"0px 0px contrast");
    AAStyle *speedYAxisTitleStyle = AAStyle.new
    .colorSet(@"#AB39E2")
    .fontSizeSet(@"14px")
    .fontWeightSet(AAChartFontWeightTypeBold)
    .textOutlineSet(@"0px 0px contrast");
    AAYAxis *tempYAxis = AAYAxis.new
    .visibleSet(true)
    .lineWidthSet(@1)
    .labelsSet(aaLables)
    .gridLineWidthSet(@1)
    .gridLineColorSet(@"#DEE2EB")
    .gridLineDashStyleSet(AAChartLineDashStyleTypeDash)
    .titleSet(AAAxisTitle.new
              .textSet(@"")
              .styleSet(tempYAxisTitleStyle));
    AAYAxis *speedYAxis = AAYAxis.new
    .visibleSet(true)
    .lineWidthSet(@1)
    .labelsSet(aaLables)
    .gridLineColorSet(@"#DEE2EB")
    .gridLineDashStyleSet(AAChartLineDashStyleTypeDash)
    .gridLineWidthSet(@1)
    .minSet(@0)
    .minRangeSet(@1)
    .titleSet(AAAxisTitle.new
              .textSet(@"")
              .styleSet(speedYAxisTitleStyle))
    .oppositeSet(true);
    AAStyle *toolStyle = AAStyle.new
    .colorSet(@"#ffffff")
    .fontSizeSet(@"12px")
    .fontWeightSet(AAChartFontWeightTypeRegular)
    .textOutlineSet(@"0px 0px contrast");
    AATooltip *aaTooltip = AATooltip.new
    .backgroundColorSet([NSString stringWithFormat:@"rgba(0,0,0,0.7)"])
    .borderRadiusSet(@4)
    .borderWidthSet(@0)
    .styleSet(toolStyle)
    .enabledSet(true)
    .sharedSet(true);
    AAMarker *marker = AAMarker.new
    .radiusSet(@2)
    .symbolSet(AAChartSymbolTypeCircle)
    .enabledSet(true);
   AASeriesElement *temp1Element = AASeriesElement.new
    .typeSet(AAChartTypeLine)
    .markerSet(marker)
    .colorSet(@"#0295E0")
    .nameSet(@"温度1(°C)")
    .yAxisSet(@0)
    .zIndexSet(@11)
    .dataSet([self.temp1Arr copy]);
    AASeriesElement *temp2Element = AASeriesElement.new
     .typeSet(AAChartTypeLine)
     .markerSet(marker)
     .colorSet(@"#63B81B")
     .nameSet(@"温度2(°C)")
     .yAxisSet(@0)
     .zIndexSet(@12)
     .dataSet([self.temp2Arr copy]);

    AASeriesElement *temp3Element = AASeriesElement.new
     .typeSet(AAChartTypeLine)
     .markerSet(marker)
     .colorSet(@"#03ABA3")
     .nameSet(@"温度3(°C)")
     .yAxisSet(@0)
     .zIndexSet(@13)
     .dataSet([self.temp3Arr copy]);
    AASeriesElement *temp4Element = AASeriesElement.new
     .typeSet(AAChartTypeLine)
     .markerSet(marker)
     .colorSet(@"#EFAB07")
     .nameSet(@"温度4(°C)")
     .yAxisSet(@0)
    .zIndexSet(@14)
     .dataSet([self.temp4Arr copy]);
    AASeriesElement *speedElement = AASeriesElement.new
     .typeSet(AAChartTypeLine)
    .markerSet(marker)
    .colorSet(@"#AB39E2")
    .nameSet(@"车速(km/h)")
    .yAxisSet(@1)
    .zIndexSet(@10)
    .dataSet([self.speedArr copy]);
    NSMutableArray *elementArr = [NSMutableArray array];
    [elementArr removeAllObjects];
    [elementArr addObject:speedElement];
    [elementArr addObject:temp1Element];
    [elementArr addObject:temp2Element];
    [elementArr addObject:temp3Element];
    [elementArr addObject:temp4Element];

    AALegend *legend = AALegend.new
    .enabledSet(true);

    AAOptions *aaOptions = AAOptions.new
     .chartSet(aaChart)
    .legendSet(legend)
    .titleSet(aaTitle)
    .xAxisSet(aaXAxis)
    .yAxisSet((id)@[tempYAxis,speedYAxis])
    .tooltipSet(aaTooltip)
    .seriesSet([elementArr copy]);

    return aaOptions;
}

- (NSMutableArray *)timeArr{

    if(!_timeArr){
        _timeArr = [NSMutableArray array];
    }
    return _timeArr;

}

- (NSMutableArray *)temp1Arr{

    if(!_temp1Arr){

        _temp1Arr = [NSMutableArray array];
    }
    return _temp1Arr;

}

- (NSMutableArray *)temp2Arr {

    if(!_temp2Arr){
        _temp2Arr = [NSMutableArray array];
    }
    return _temp2Arr;
}

- (NSMutableArray *)temp3Arr{
    if(!_temp3Arr){
        _temp3Arr = [NSMutableArray array];
    }
    return _temp3Arr;
}

- (NSMutableArray *)temp4Arr{
    if(!_temp4Arr){
        _temp4Arr = [NSMutableArray array];
    }
    return _temp4Arr;
}

- (NSMutableArray *)speedArr{
    if(!_speedArr){
        _speedArr = [NSMutableArray array];
    }
    return _speedArr;
}

@AAChartModel

solanderLK commented 1 year ago

不知道是不是在数据量上面做了处理?数据量少的点就不显示了?还是因为屏幕像素点只能展示那么多的点,从而没有画出来?

从我上面的测试示例中来看, 如果 data 的元素个数差异在21~1550之间, 应该还是能够显示所有 AASeriesElement 的.

是我没有表述清楚,数值差异 是指真的有值的差异,但是其数组的大小是一致的,只是其他数值是空字符串("")

solanderLK commented 1 year ago

我后面又实验了几种情景,发现问题并不是仅仅是数据数量导致的,也可能是数据源的问题。 我将数据截取展示,发现,只要temp4Arr数据超过1000 条,temp4Arr这条数据就不展示了。只有这条数据中包含了"" 字符串。后面又在另外数据源中加了"",也同样复现了该问题,超过了一定的数量值,就会消失,目前实验是1000条,超过1000条,含有”“的数据线条消失了;这种问题 有解决办法吗? 是什么样的机制会导致这个问题出现呢。

@AAChartModel

AAChartModel commented 1 year ago

你把你的空字符串 "", 改成 NSNull.new试试.

类似于这样:

    NSArray *dataArr = @[
        @0.45, NSNull.new, NSNull.new,
        @0.55, @0.58, @0.62, NSNull.new, NSNull.new,
        @0.56, @0.67, @0.50, @0.34, @0.50, NSNull.new, NSNull.new, NSNull.new, NSNull.new,
        @0.23, @0.47, @0.46, @0.38, @0.56, @0.48, @0.36, NSNull.new, NSNull.new, NSNull.new, NSNull.new, NSNull.new, NSNull.new, NSNull.new, NSNull.new,
        @0.74, @0.66, @0.65, @0.71, @0.59, @0.65, @0.77, @0.52, @0.53, @0.58, @0.53,
    ];
solanderLK commented 1 year ago

你把你的空字符串 "", 改成 NSNull.new试试.

类似于这样:

    NSArray *dataArr = @[
        @0.45, NSNull.new, NSNull.new,
        @0.55, @0.58, @0.62, NSNull.new, NSNull.new,
        @0.56, @0.67, @0.50, @0.34, @0.50, NSNull.new, NSNull.new, NSNull.new, NSNull.new,
        @0.23, @0.47, @0.46, @0.38, @0.56, @0.48, @0.36, NSNull.new, NSNull.new, NSNull.new, NSNull.new, NSNull.new, NSNull.new, NSNull.new, NSNull.new,
        @0.74, @0.66, @0.65, @0.71, @0.59, @0.65, @0.77, @0.52, @0.53, @0.58, @0.53,
    ];

确实解决了问题,但是我还想弄明白为什么和数量有关系呢。

AAChartModel commented 1 year ago

具体原因我也不太清楚.

不过空字符串用来表示空数据, 在 Highcharts 当中不是规范的写法, 所以数据量低的时候, Highcharts 还可以做兼容, 数据量过大的时候, 可能超过了 Highcharts 的容错范围边界了.

当然了, 这纯属我瞎猜.