0
votes

I'm kind of stumped here. I've looked at the docs of MPAndroidChart and a bunch of questions being asked here, but I can't quite figure out what's causing it.

Basically, unless I have 12 or so labels, or I just force label counts, there are no Mondays in my charts. The labels are placed sunday evening and early tuesday morning consistently, which causes monday not to be drawn. Ideally, at a certain zoom level I should see months, then days, then hours, but the labels would have to be at logical places (1st day of month, midnight, start of hours, etc...)

What I'd love to do is to be able to force specific intervals for the xLabels. ex: at a certain zoom level have intervals of 24 hours, 1 hour, or 15 mins, but all starting at midnight so that there wouldn't be any weird time labels. So far I haven't found a single confirmed solution for this... at least not with real, and irregular Epoch timestamps. This very similar question was asked, but it didn't lead anywhere: MPAndroidChart x-axis date/time label formatting

18362047_10155310951108420_632358_o

(no mondays, unless you zoom into the hourly view, where they become visible if I force the label formatting to show days)

I'm graphing various data points, but their x values are ALL unix timestamps.

my code:

in my onCreate:

//        creates line graphs for al DPs
        for (int i = 0; i < activeDataPointIDs.size(); i++) {
//            gets id and dataPointLogic
            int id = DataPointsManager.activeDataPointIDs.get(i);
            DataPointLogic logic = DataPointsManager.getDataPointLogic(id);

//          creates the chart
            LineChart lineChart;
            lineChart = createLineGraph(logic, UnitConverter.dpToPixel(300, this));

    //            creates a label
            TextView textView = new TextView(this);
            textView.setText(DataPointsManager.dataPointNames[id]);


    //            adds the chart and label to the containing view
            mainContainer.addView(textView);
            mainContainer.addView(lineChart);

    //            adds the chart to a list accessible within the activity class
            lineChartList.add(lineChart);

        }


        calculateGraphRange();

calculateGraphRange() :

  //    calculates the graph range based on the time interval selected
        void calculateGraphRange() {

        int days = CompassApp.daysToShowStats;


    //        figuring out stupid timezone offsets
        long baseOffset = TimeZone.getDefault().getRawOffset();
        long dstOffset = TimeZone.getDefault().getDSTSavings();
        long timezoneOffset = baseOffset + dstOffset;

    //        default values are set for 1 week, TODO: deal with timezones
        long graphMax = System.currentTimeMillis();
        long graphMaxExtra = graphMax % TimeUtilities.UNIX_DAY;
        graphMax = graphMax - graphMaxExtra + TimeUtilities.UNIX_DAY - timezoneOffset;
        long graphMin = graphMax - (TimeUtilities.UNIX_DAY * days);
    //

    //        prevents invalid graphs from being drawn //TODO: fix labels when range is 0 or null
        if (days == 0) {
            graphMax = 0;
            graphMin = 0;
        }

    //        applies to all charts
        for (int i = 0; i < lineChartList.size(); i++) {

            LineChart chart = lineChartList.get(i);

            chart.getXAxis().setAxisMaximum(graphMax);
            chart.getXAxis().setAxisMinimum(graphMin);


            chart.notifyDataSetChanged();
            chart.invalidate();

        }


    }

Nothing funky in the date formatter. most is commented out to make testing easier

 IAxisValueFormatter createDateFormatter() {
        IAxisValueFormatter formatter = new IAxisValueFormatter() {


            @Override
            public String getFormattedValue(float value, AxisBase axis) {
                Date date = new Date((long) value);

                SimpleDateFormat fmt;


//                switch (labelModeSelected) {
//                    case HOURS_FORMAT:
//                        fmt = new SimpleDateFormat("h:mm a");
//                        break;
//
//                    case DAYS_FORMAT:
//                        fmt = new SimpleDateFormat("E d");
//                        break;
//
//                    case WEEKS_FORMAT:
//                        fmt = new SimpleDateFormat("d MMM");
//                        break;
//
//                    case MONTHS_FORMAT:
//                        fmt = new SimpleDateFormat("MMM yyyy");
//                        break;
//
//                    case YEARS_FORMAT:
//                        fmt = new SimpleDateFormat("yyyy");
//
//                        break;
//
//                    default:
//                        fmt = new SimpleDateFormat("E d MMM");
//                        break;
//                }


                fmt = new SimpleDateFormat("MMM d H:mm zz"); //TODO remove after tests and add switch
                fmt.setTimeZone(TimeZone.getDefault()); // sets time zone... I think I did this properly...


                String s = fmt.format(date);


                return s;
            }

            // we don't draw numbers, so no decimal digits needed
            public int getDecimalDigits() {
                return 0;
            }


        };

        return formatter;
    }

method to create my line graphs

    LineChart createLineGraph(DataPointLogic dataPointLogic, int height) {


        final LineChart lineChart = new LineChart(this);

        lineChart.setMinimumHeight(height);


        List<Entry> entries = new ArrayList<Entry>();

        List<Number> xList = new ArrayList<>();
        List<Number> yList = new ArrayList<>();

        xList = dataPointLogic.getXVals(); //TODO: does this just not work with the class inheritance
        yList = dataPointLogic.getYVals(); //TODO: does this just not work with the class inheritance


        for (int i = 0; i < xList.size(); i++) {

            long x = xList.get(i).longValue();
            float y = yList.get(i).floatValue();

            entries.add(new Entry(x, y));
        }

//
        final LineDataSet dataSet = new LineDataSet(entries, "Time series");
//        graph smoothing params
        dataSet.setCubicIntensity(0.5f);
        dataSet.setMode(LineDataSet.Mode.HORIZONTAL_BEZIER);

//        graph fill params
        dataSet.setDrawFilled(true);
//        dataSet.setFillDrawable(ContextCompat.getDrawable(this, R.drawable.doge));
        dataSet.setFillColor(primaryColor);

//        line params
        dataSet.setColor(primaryColor);
        dataSet.setLineWidth(1f);

//        circle params
        dataSet.setCircleRadius(2.5f);
        dataSet.setCircleColor(primaryColor);
        dataSet.setDrawCircleHole(false);


        LineData lineData = new LineData(dataSet);
        lineData.setDrawValues(false);


        lineChart.setData(lineData);
//
//        applies the timestamp formatting
        XAxis xAxis = lineChart.getXAxis();
        xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
        xAxis.setValueFormatter(createDateFormatter());
        xAxis.setDrawLabels(true);
        xAxis.setCenterAxisLabels(true);
        xAxis.setLabelRotationAngle(90f); // rotates label so we can see it all TODO remove after tests


        YAxis yAxisRight = lineChart.getAxisRight();
        yAxisRight.setDrawLabels(false);
        yAxisRight.setDrawGridLines(false);

        YAxis yAxisLeft = lineChart.getAxisLeft();
        yAxisLeft.setDrawGridLines(false);
        yAxisLeft.setSpaceBottom(0.0f);

//        removes description and legend
        lineChart.getLegend().setEnabled(false);
        lineChart.getDescription().setEnabled(false);

//        sets the maximum zoom... must be provided unix time values
        lineChart.setVisibleXRangeMinimum(TimeUtilities.UNIX_HOUR);

        lineChart.setOnChartGestureListener(new OnChartGestureListener() {
            @Override
            public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {

            }

            @Override
            public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {

            }

            @Override
            public void onChartLongPressed(MotionEvent me) {

            }

            @Override
            public void onChartDoubleTapped(MotionEvent me) {

            }

            @Override
            public void onChartSingleTapped(MotionEvent me) {

            }

            @Override
            public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) {

            }

            @Override
            public void onChartScale(MotionEvent me, float scaleX, float scaleY) {
                scaleChartLabels(lineChart);
            }

            @Override
            public void onChartTranslate(MotionEvent me, float dX, float dY) {

            }
        });

//        applies the scale
        scaleChartLabels(lineChart);


        lineChart.invalidate(); // refreshes data

        return lineChart;
    }

method change how the labels are displayed, depending on the scale of the graph

  //    changes the scale of the chart labels depending on what is selected
    void scaleChartLabels(LineChart chart) {
        float max = chart.getHighestVisibleX();
        float min = chart.getLowestVisibleX();
        float totalXVisible = max - min;


        float granularity = TimeUtilities.UNIX_DAY;

        int labelCount = 0;

        //        conditional switch to determine display params of graphs
        if (totalXVisible < TimeUtilities.UNIX_DAY) {
            labelCount = 8;
            granularity = TimeUtilities.UNIX_HOUR;
            labelModeSelected = HOURS_FORMAT;
            // Day view
        } else if (totalXVisible < TimeUtilities.UNIX_DAY * 2) {
            labelCount = 2;
            granularity = TimeUtilities.UNIX_DAY;
            labelModeSelected = DAYS_FORMAT;
            // Week view
        } else if (totalXVisible < TimeUtilities.UNIX_DAY * 3) {
            labelCount = 3;
            granularity = TimeUtilities.UNIX_DAY;
            labelModeSelected = DAYS_FORMAT;
            // Week view
        } else if (totalXVisible < TimeUtilities.UNIX_DAY * 4) {
            labelCount = 4;
            granularity = TimeUtilities.UNIX_DAY;
            labelModeSelected = DAYS_FORMAT;
            // Week view
        } else if (totalXVisible < TimeUtilities.UNIX_DAY * 5) {
            labelCount = 5;
            granularity = TimeUtilities.UNIX_DAY;
            labelModeSelected = DAYS_FORMAT;
            // Week view
        } else if (totalXVisible < TimeUtilities.UNIX_DAY * 6) {
            labelCount = 6;
            granularity = TimeUtilities.UNIX_DAY;
            labelModeSelected = DAYS_FORMAT;
            // Week view
        } else if (totalXVisible < TimeUtilities.UNIX_WEEK) {
            labelCount = 7;
            granularity = TimeUtilities.UNIX_DAY;
            labelModeSelected = DAYS_FORMAT;
            // Week view
        } else if (totalXVisible < (TimeUtilities.UNIX_WEEK * 2)) {
            labelCount = 7;
            granularity = TimeUtilities.UNIX_DAY;
            labelModeSelected = DAYS_FORMAT;
            // Week view
        } else if (totalXVisible < TimeUtilities.UNIX_MONTH) {
            labelCount = 7;
            granularity = TimeUtilities.UNIX_DAY;
            labelModeSelected = WEEKS_FORMAT;
            //Month view
        } else if (totalXVisible < TimeUtilities.UNIX_MONTH * 3) {
            labelCount = 3;
            granularity = TimeUtilities.UNIX_MONTH;
            labelModeSelected = MONTHS_FORMAT;

            // 90 day view
        } else if (totalXVisible < TimeUtilities.UNIX_YEAR) {
            labelCount = 6;
            granularity = TimeUtilities.UNIX_MONTH;
            labelModeSelected = MONTHS_FORMAT;

            // year view
        } else if (totalXVisible > TimeUtilities.UNIX_YEAR) {
            Log.w(TAG, "case not implemented yet");
        }

        chart.getXAxis().setGranularityEnabled(true);
        chart.getXAxis().setGranularity(granularity);


        chart.getXAxis().setLabelCount(labelCount);
    }
2
can you please try following things to get your required output. stackoverflow.com/a/40806503/1343788 - Mehul Kabaria
according to the comments it shows the right labels but in the wrong locations, which won't work for what I'm doing. - JCLaHoot

2 Answers

3
votes

Turns out, since the Entries use floats, I was losing precision on my timestamps... enough to mess up the intervals between labels on the x axis.

I just divided all of them by 60 000 as a quick test, to get my timestamps in millis into timestamps in minutes and that seemed to do the trick.

0
votes

i had the same problem, i solved it by

1.setting the

xAxis.setAxisMinimum(minimumPoint)

and

xAxis.setAxisMaximum(maximumPoint)

to correspond with the starting point and the ending point respectively.

  1. then use this overloaded method `

xAxis.setLabelCount(length, true)

`, where length is the size of the label array.