Range selector and aggreation chart in one

Posted by: a.sharov on 6 November 2021, 9:21 am EST

    • Post Options:
    • Link

    Posted 6 November 2021, 9:21 am EST

    Hi.

    I have a control with 2 flex chart – one for main data, and other is range selector + aggregation chart. My data is mostly hour data (from 5 mins to hours). I want to use range selector chart as aggregation chart also, where I aggregate (avg) by day, month and year. Currently my code is based on your samples from FlexChartExploer, namely RangeSelector.cs and Aggregation.cs . The problem arise in following code when I especially when I choose month or year aggregation:

    
            private void SetupRangeSelector()
            {
                if(_rsXRangeSelector != null)
                    return;
                
                _rsXRangeSelector = new RangeSelector(fcChartRangeSelector);
                _rsXRangeSelector.ValueChanged += (s, e) =>
                {
                    
                    flexChart3.AxisX.Min =  _rsXRangeSelector.LowerValue;
                    flexChart3.AxisX.Max =   _rsXRangeSelector.UpperValue;
                    Debug.WriteLine($"{_rsXRangeSelector.LowerValue}-{_rsXRangeSelector.UpperValue}");
    
                };
            }
    
    

    Suppose that my data have date DateTime.Now + 24 month (2 years) from now hourly points. I want to aggregate this chart and display in range selector. It works but in code above I have values from 0 to 24 (number of month). Or from 0 to 2 for years.

    Example (month averaging for 2 years hourly data):

    _rsXRangeSelector.LowerValue = 0.27

    _rsXRangeSelector.UpperValue = 20.905;

    But values in flexChart3.AxisX.ActualMax is of order 44500 (number of days since 1900 I believe). So everything in main chart is got broken.

    I guess it is not a big deal to write conversion from decimal datetime to actual datetime, but maybe C1 has appropriate utils or someone know how to do it?

    Consider month avg. and value 20.905. I understand that 20 is a month, so I can add it to my min date (Addmonth(20)) , but what is .905 (or 905) is it total hours?

    Thanks in advance.

  • Posted 8 November 2021, 3:02 pm EST

    Anyone?

  • Posted 9 November 2021, 5:41 am EST

    Hi Anton,

    I apologize for the delay.

    We are working on it and will get back to you as soon as possible.

    Regards,

    Kartik

  • Posted 9 November 2021, 4:09 pm EST

    Hi.

    For now I have following:

    
     private void SetupRangeSelector()
            {
                if (_rsXRangeSelector != null)
                {
                    return;
                }
    
                _rsXRangeSelector = new RangeSelector(fcChartRangeSelector);
                _rsXRangeSelector.ValueChanged += (s, e) =>
                {
                   
                    flexChart3.AxisX.Min = _fdsi.GetNeededDate(fcChartRangeSelector.AxisX.TimeUnit,
                            _rsXRangeSelector.LowerValue);
                    flexChart3.AxisX.Max = _fdsi.GetNeededDate(fcChartRangeSelector.AxisX.TimeUnit,  _rsXRangeSelector.UpperValue);
                    Debug.WriteLine($"before:{_rsXRangeSelector.LowerValue}-{_rsXRangeSelector.UpperValue}");
                    Debug.WriteLine($"after:{ flexChart3.AxisX.Min }-{flexChart3.AxisX.Max}");
                };
             
            }
    
    ...
    
        internal class FlexDataSourceInfo
        {
            private readonly List<XPosition> _data;
            private readonly DateTime _startYear = new DateTime(1900,1,1);
    
            public FlexDataSourceInfo([NotNull] List<XPosition> data)
            {
                _data = data ?? throw new ArgumentNullException(nameof(data));
                if (data.Count == 0)
                {
                    throw new ArgumentException("No data points.");
                }
            }
    
            public double GetNeededDate(TimeUnits units, double value)
            {
                var minDate = _data[0].Date;
    
                double result;
                switch(units)
                {
                    case TimeUnits.Day:
                    
                        var rangeDays = (int)Math.Truncate(value);
                        var dq = minDate.AddDays(rangeDays);
                        var hourDiff = value - rangeDays;
                        Debug.Assert(hourDiff >= 0, "hourDiff >= 0");
                        var hours = (int)(24 * hourDiff);
                        var z = (int)(new DateTime(dq.Year, dq.Month, dq.Day, hours, 0, 0) - _startYear).TotalDays;
                        result = z + hours / 100.0;
                        
                        break;
    
    
                    case TimeUnits.Month:
                        var month = (int)Math.Truncate(value);
                        var mq = minDate.AddMonths(month);
                        var monthDiff = value - month;
                        Debug.Assert(monthDiff >= 0, "monthDiff >= 0");
                        var days = (int)(DateTime.DaysInMonth(mq.Year, mq.Month) * monthDiff);
                        if (days == 0)
                        {
                            days = 1;
                        }
                        result = (int)(new DateTime(mq.Year, mq.Month, days) - _startYear).TotalDays;
                        
                        break;
    
    
                    case TimeUnits.Year:
                        var years = (int)Math.Truncate(value);
                        var yq = minDate.AddYears(years);
                        var yearsFraction = value - years;
                        Debug.Assert(yearsFraction >= 0, "yearsFraction >= 0");
                        var months = (int)(12 * yearsFraction);
                        if (months == 0)
                        {
                            months = yq.Month;
                        }
    
                        result = (int)(new DateTime(yq.Year, months, 1) - _startYear).TotalDays;
                        
                        break;
                        
    
                    default:
                        throw new ArgumentOutOfRangeException(nameof(units), units, null);
                }
    
            
                return result;
            }
        }
    
    

    Consider method GetNeededDate and it’s switch. I generate data for say two years, hourly, so my month runs from 0 to 24. Say I got for some side of range selector value 7.843 (or similiar fractional number wher integer part from 0 to 24), I have problems interpretating fractional part – what is how was it obtained? Look at my code for TimeUnits.Day and TimeUnits.Month, I I think that I treat fractional part completely wrong but this code somehow works, I see aggregated chart for days and month for two years of hour data, probably not so precise, because I loose precision in my code, but it somehow works. But year part and my code (TimeUnits.Year) doesn’t wokr at all… I have no idea what to do there, how recover more data then a year?

    Put it simple, suppose I have some amount of hour data (two month from tody), I aggregate them by days and see following values for code

    
      Debug.WriteLine($"before:{_rsXRangeSelector.LowerValue}-{_rsXRangeSelector.UpperValue}");
    
    

    before:14,0229885057471-61

    before:16,0387931034483-61

    before:19,8951149425287-61

    before:20,1580459770115-61

    before:20,1580459770115-60,2112068965517

    before:20,1580459770115-56,3548850574713

    before:20,1580459770115-54,3390804597701

    Quite logical for me except fractional part and it’s representation.

    Monthly:

    before:0,378223495702006-2

    before:0,495702005730659-2

    before:0,553008595988539-2

    before:0,578796561604584-2

    before:0,578796561604584-1,99713467048711

    before:0,578796561604584-1,79656160458453

    Seems logical to me – span for 2 month, but what is in fraction?

    Year:

    before:0,249645390070922-1

    before:0,327659574468085-1

    before:0,327659574468085-0,970212765957447

    before:0,327659574468085-0,862411347517731

    before:0,327659574468085-0,788652482269504

    Year range is for current and next 2022, so from 0 to 1, but have no idea what is in fraction…

    I hope you get the idea, looking for your response.

  • Posted 10 November 2021, 1:32 am EST

    Hi Anton,

    Thank you for sharing the detailed description.

    JFYI, FlexChart’s DateTime axis uses the OLE representation of a Date which you can get by using DateTime’s ToOADate method. So, when your range selector chart is aggregated you need to convert its UpperValue/LowerValue to the corresponding DateTime (OLE) before setting Axis’s Min/Max properties.

    For example, you can convert value range from 1 to 12 to its DateTime representation as follows:

    
    public static DateTime ConvertToDateTime(int year, double value)
    {
    
                int month = (int)Math.Floor(value); // month        
                var totalMonthMilliseconds = DateTime.DaysInMonth(year, month) * MillisecondsInDay; // total milliseconds in this month
                TimeSpan timespan = new TimeSpan(0);
    
                if(value.ToString().Contains('.')) // if value is decimal
                {
                    var decimalPart = value % 1; // gets the decimal part to get the elapsed time for this month (we will treat the decimal part as milliseconds)
                    timespan = timespan.Add(TimeSpan.FromMilliseconds(totalMonthMilliseconds * decimalPart)); // gets the elapsed timespan for this month
    
                    /// NOTE : You can use this approach to convert year range to date.
                }
    
                var date = new DateTime(year, month, 1, 0, 0, 0).Add(timespan); // add timespan to the first date of month to get the actual date         
                return date;
     }
    
    

    JFYI, in the above approach I have treated the integer part as month and decimal part as the elapsed milliseconds for calculating the datetime. Just in case needed, you can modify the above code as per your requirement.

    Now, you can use the above method as follows:

    
    year = 2021;
    flexChart1.AxisX.Min = Converter.ConvertToDateTime(year, _rsXRangeSelector.LowerValue).ToOADate();
    flexChart1.AxisX.Max = Converter.ConvertToDateTime(year, _rsXRangeSelector.UpperValue).ToOADate();
    
    

    Please refer to the same from the attached sample. (see FlexChartRange.zip)

    Best Regards,

    Kartik

    FlexChartRange.zip

  • Posted 10 November 2021, 10:52 am EST

    Hi, thank you for reply!

    I’ve slightly changed your code because there could be montyly aggregation for serveal years, hence month can range, for example, from 0 to 36.

    Can you give a look at my code, especially on year conversion, cause I’m not usre I hadle decimal part correctly:

    
     private static DateTime ConvertMonthAggToDateTime(DateTime start, double value)
            {
    
                int month = (int)Math.Truncate(value); // month        
                var temp = start.AddMonths(month);
                var totalMonthMilliseconds = DateTime.DaysInMonth(temp.Year, temp.Month) * MILLISECONDS_IN_DAY; // total milliseconds in this month
                TimeSpan timespan = new TimeSpan(0);
    
                var decimalPart = value - month;
                Debug.Assert(decimalPart >= 0, "decimalPart >= 0");
                timespan = timespan.Add(
                        TimeSpan.FromMilliseconds(totalMonthMilliseconds *
                                                  decimalPart)); // gets the elapsed timespan for this month
    
                var date = new DateTime(temp.Year, temp.Month, 1, 0, 0, 0).Add(timespan); // add timespan to the first date of month to get the actual date      
                return date;
            }
    
    
            private static DateTime ConvertDayAggToDateTime(DateTime start, double value)
            {
    
                int days = (int)Math.Truncate(value);        
                var temp = start.AddDays(days);
                var totalDayMilliseconds = MILLISECONDS_IN_DAY; 
                TimeSpan timespan = new TimeSpan(0);
    
                var decimalPart = value - days;
                Debug.Assert(decimalPart >= 0, "decimalPart >= 0");
                timespan = timespan.Add(
                        TimeSpan.FromMilliseconds(totalDayMilliseconds *
                                                  decimalPart)); // gets the elapsed timespan for this month
    
                var date = new DateTime(temp.Year, temp.Month, temp.Day).Add(timespan); // add timespan to the first date of month to get the actual date      
                return date;
            }
    
            private static DateTime ConvertYearAggToDateTime(DateTime start, double value)
            {
    
                int year = (int)Math.Truncate(value);       
                var tempYear = start.AddYears(year);
                var daysInYear = DateTime.IsLeapYear(tempYear.Year) ? 366 : 365;
                long totalYearMilliseconds =     (long)(daysInYear * MILLISECONDS_IN_DAY); // total milliseconds in this month
                TimeSpan timespan = new TimeSpan(0);
    
                var decimalPart = value - year;
                Debug.Assert(decimalPart >= 0, "decimalPart >= 0");
                timespan = timespan.Add(
                        TimeSpan.FromMilliseconds(totalYearMilliseconds *
                                                  decimalPart)); 
    
                var date = new DateTime(tempYear.Year, 1, 1, 0, 0, 0).Add(timespan); 
                return date;
            }
    
    

    I still can’t undestand difference in following situation:

    consider month range from 0-2, say 1.30654… and the very same

    range and value for year – 1.30654… .

    It is the same value, except that integer part represent either month or year, and I know that it is either month or year, but how to correctly handle decimal part?

    For month it is total milliseconds elapsed, from day 1 of month, correct?

    What is it according to year? I treated it as fraction of milliseconds of year ,

    namely

    
      TimeSpan.FromMilliseconds(totalYearMilliseconds *  decimalPart)); 
    
    

    (see code above for year).

  • Posted 11 November 2021, 12:42 am EST

    Hi Anton,

    Thank you for sharing the snapshot.

    > For month it is total milliseconds elapsed, from day 1 of month, correct?

    Yes, you are right,

    > What is it according to year? I treated it as fraction of milliseconds of year

    Just like in month, the decimal part in year range represents the elapsed milliseconds from the first day of the year.

    Although your code is correct. But I have attached the modified sample showing the same with year aggregation. (see FlexChartRange_Mod.zip)

    Best Regards,

    Kartik

    FlexChartRange_Mod.zip

Need extra support?

Upgrade your support plan and get personal unlimited phone support with our customer engagement team

Learn More

Forum Channels