Search code examples
androidgraphchartsmpandroidchartandroid-graphview

Intersection point for two series/ line charts in graph


I want to mark points of intersection of two lines in x-y plane on a graph..

Using GraphView in my app, I have plotted 2 series in following manner:

GraphView graph = (GraphView) findViewById(R.id.graph);
graph.getViewport().setScalable(true);
graph.getViewport().setScrollable(true);
graph.getViewport().setScalableY(true);
graph.getViewport().setScrollableY(false);

PointsGraphSeries<DataPoint> series1 = new PointsGraphSeries<>(new DataPoint[]{
        new DataPoint(3, 4),
        new DataPoint(4, 4.1),
        new DataPoint(5, 4.1),
        new DataPoint(6, 4.2),
        new DataPoint(7, 4.2),
        new DataPoint(8, 4.3),
        new DataPoint(9, 4.3),
        new DataPoint(10, 4.4),
        new DataPoint(10, 4.4),
        new DataPoint(10, 4.5),
        new DataPoint(11, 4.5),
        new DataPoint(12, 4.5),
        new DataPoint(12, 4.6),
        new DataPoint(13, 4.6),
        new DataPoint(14, 4.6),
        new DataPoint(15, 4.7),
        new DataPoint(16, 4.8),
        new DataPoint(16, 5.0),
        new DataPoint(17, 5.0),
        new DataPoint(18, 5.1),
        new DataPoint(19, 5.4),
        new DataPoint(20, 5.5),
        new DataPoint(21, 5.6),
        new DataPoint(22, 5.6),
        new DataPoint(25, 5.7),
        new DataPoint(26, 5.7),
        new DataPoint(27, 5.8),
        new DataPoint(28, 5.8),
        new DataPoint(29, 5.9),
        new DataPoint(30, 5.9),
        new DataPoint(30, 6.0),
        new DataPoint(31, 6.0),
        new DataPoint(32, 6.0)
});
graph.addSeries(series1);
series1.setShape(PointsGraphSeries.Shape.POINT);
series1.setColor(Color.BLACK);

PointsGraphSeries<DataPoint> series2 = new PointsGraphSeries<DataPoint>(new DataPoint[]{
        new DataPoint(3, 5),
        new DataPoint(4, 4.9),
        new DataPoint(5, 4.8),
        new DataPoint(6, 4.7),
        new DataPoint(7, 4.6),
        new DataPoint(8, 4.5),
        new DataPoint(9, 4.4),
        new DataPoint(10, 4.3),
        new DataPoint(11, 4.2),
        new DataPoint(12, 4.1),
        new DataPoint(13, 4.0),
        new DataPoint(14, 3.9),
        new DataPoint(15, 3.8),
        new DataPoint(16, 3.7),
        new DataPoint(17, 3.6),
        new DataPoint(18, 3.5),
        new DataPoint(19, 3.4),
        new DataPoint(20, 3.3),
        new DataPoint(21, 3.2),
        new DataPoint(22, 3.1),
        new DataPoint(23, 3.0),
        new DataPoint(24, 2.9),
        new DataPoint(25, 2.7),
        new DataPoint(26, 2.6),
        new DataPoint(27, 2.5),
        new DataPoint(28, 2.4),
        new DataPoint(29, 2.3),
        new DataPoint(30, 2.2),
        new DataPoint(31, 2.1),
        new DataPoint(32, 2.0),
        new DataPoint(33, 1.9),
        new DataPoint(34, 1.8),
        new DataPoint(35, 1.7),
        new DataPoint(36, 1.6),
        new DataPoint(37, 1.5),
        new DataPoint(38, 1.4),
        new DataPoint(39, 1.2),
        new DataPoint(40, 1.1),
        new DataPoint(42, 0.9),
        new DataPoint(43, 0.9),
        new DataPoint(44, 1.0),
        new DataPoint(45, 1.0),
        new DataPoint(46, 1.0),
        new DataPoint(47, 1.0),
        new DataPoint(48, 1.0),
        new DataPoint(49, 1.0),
        new DataPoint(50, 1.0),
        new DataPoint(51, 1.0),
        new DataPoint(52, 1.0),
        new DataPoint(53, 1.0),
        new DataPoint(54, 1.0),
        new DataPoint(55, 1.0),
        new DataPoint(56, 1.0),
        new DataPoint(57, 1.0),
        new DataPoint(58, 1.0),
        new DataPoint(59, 1.0),
        new DataPoint(60, 1.0),
});
graph.addSeries(series2);
series2.setShape(PointsGraphSeries.Shape.POINT);
series2.setColor(Color.MAGENTA);

Update:- No two DataPoints are equal.

How to mark the intersection of two lines/series in graph with a label?

Is it possible using GraphView / MPAndroidChart or any other library?

Any reply/comment/reference to solution would be greatly appreciated.

I want to implement something similar to as shown in this image: enter image description here

The intersections here are marked with red-dot and accompany a label.


Solution

  • Thank you @svahidhoss for your post. It helped me find solution for the problem.

    I have used your sample and added if condition for cases when no DataPoints are equal.

    Below is the solution:

                DataPoint[] pointsA = new DataPoint[]{
                new DataPoint(1, 2500),
                new DataPoint(2, 2500),
                new DataPoint(3, 2500),
                new DataPoint(4, 2500),
                new DataPoint(5, 3000),
                new DataPoint(6, 3000),
                new DataPoint(7, 3000),
                new DataPoint(8, 3000),
                new DataPoint(9, 3500),
                new DataPoint(10, 3500),
                new DataPoint(11, 3500),
                new DataPoint(12, 3500),
                new DataPoint(13, 4000),
                new DataPoint(14, 4000),
                new DataPoint(15, 4000),
                new DataPoint(16, 4000),
                new DataPoint(17, 4500),
                new DataPoint(18, 4500),
                new DataPoint(19, 4500),
                new DataPoint(20, 4500),
                new DataPoint(21, 5000),
                new DataPoint(22, 5000),
                new DataPoint(23, 5000),
                new DataPoint(24, 5500),
                new DataPoint(25, 5500),
                new DataPoint(26, 5700),
                new DataPoint(27, 6000)
        };
    
        DataPoint[] pointsB = new DataPoint[]{
                new DataPoint(1, 3500),
                new DataPoint(2, 3400),
                new DataPoint(3, 3300),
                new DataPoint(4, 3200),
                new DataPoint(5, 3200),
                new DataPoint(6, 3200),
                new DataPoint(7, 3100),
                new DataPoint(8, 3100),
                new DataPoint(9, 3000),
                new DataPoint(10, 3000),
                new DataPoint(11, 3000),
                new DataPoint(12, 3000),
                new DataPoint(13, 3000),
                new DataPoint(14, 3000),
                new DataPoint(15, 3000),
                new DataPoint(16, 3000),
                new DataPoint(17, 3000),
                new DataPoint(18, 3000),
                new DataPoint(19, 3000),
                new DataPoint(20, 2900),
                new DataPoint(21, 2900),
                new DataPoint(22, 2800),
                new DataPoint(23, 2800),
                new DataPoint(24, 2700),
                new DataPoint(25, 2700),
                new DataPoint(26, 2600)
        };
    
    PointsGraphSeries<DataPoint> seriesX = new PointsGraphSeries<>();
        seriesX.setShape(PointsGraphSeries.Shape.TRIANGLE);
        seriesX.setColor(Color.BLUE);
        seriesX.setTitle("This is X");
        plottedX = false;
    
        // If series has at least 1 equal DataPoint
        for (DataPoint a : pointsA) {
            for (DataPoint b : pointsB) {
                if (a.getX() == b.getX() && a.getY() == b.getY()) {
                    seriesX.appendData(a, true, pointsB.length);
                    plottedX = true;
                }
            }
        }
        /**
        * If exact pair of x and y is not available in both
        */
        if (!plottedX) {
            DataPoint plot = findIntersection(pointsA, pointsB);
    
            if (flagIfFoundX) {
                seriesX.appendData(plot, true, pointsB.length);
                flagIfFoundX = false;
            }
        }
    
    /**
     * Method to check whether two line segments intersect with each other
     * https://www.geeksforgeeks.org/program-for-point-of-intersection-of-two-lines/
     */
    public DataPoint findIntersection(DataPoint[] line1, DataPoint[] line2) {
    
        /**
         * https://www.geeksforgeeks.org/program-for-point-of-intersection-of-two-lines/
         *
         * determinant = a1 * b2 - a2 * b1
         * if (determinant == 0) {
         *     // Lines are parallel
         * }
         * else {
         *     x = (c1*b2 - c2*b1) / determinant
         *     y = (a1*c2 - a2*c1) / determinant
         * }
         * ---------
         * Example:
         * ---------
         * Input : A = (1, 1), B = (4, 4)
         *         C = (1, 8), D = (2, 4)
         * Output : The intersection of the given lines
         *          AB and CD is: (2.4, 2.4)
         *
         * Input : A = (0, 1), B = (0, 4)
         *         C = (1, 8), D = (1, 4)
         * Output : The given lines AB and CD are parallel.
         */
    
    
        for (int i = 0; i < line1.length; i++) {
            int nextI = i;
            nextI++;
            if (nextI == line1.length) break;
    
            for (int j = 0; j < line2.length; j++) {
                int nextJ = j;
                nextJ++;
                if (nextJ == line2.length) break;
                DataPoint d = checkIntersecting(line1[i], line1[nextI], line2[j], line2[nextJ]);
                if (flagIfFoundX) {
                    return d;
                }
            }
        }
        return null;
    }
    
    /**
     * Method to check whether segment AB and segment CD intersect with each other
     * https://www.geeksforgeeks.org/program-for-point-of-intersection-of-two-lines/
     *
     * @param A point A of segment AB
     * @param B point B of segment AB
     * @param C point C of segment CD
     * @param D point D of segment CD
     * @return DataPoint null if segments are not intersecting
     * otherwise intersection point
     */
    public DataPoint checkIntersecting(DataPoint A, DataPoint B,
                                       DataPoint C, DataPoint D) {
        // Line AB represented as a1x + b1y = c1
        double a1 = B.getY() - A.getY();
        double b1 = A.getX() - B.getX();
        double c1 = a1 * (A.getX()) + b1 * (A.getY());
    
        // Line CD represented as a2x + b2y = c2
        double a2 = D.getY() - C.getY();
        double b2 = C.getX() - D.getX();
        double c2 = a2 * (C.getX()) + b2 * (C.getY());
    
        double determinant = a1 * b2 - a2 * b1;
    
        if (determinant == 0) {
           // The lines are parallel. This is simplified
           // by returning a pair of FLT_MAX
           // return new DataPoint(Double.MAX_VALUE, Double.MAX_VALUE);
            return null;
        } else {
            double x = (b2 * c1 - b1 * c2) / determinant;
            double y = (a1 * c2 - a2 * c1) / determinant;
    
            // check if the point lies on given line segment
            /* To check if "x" is between "a" and "b";
    
              int m = (a+b)/2;
    
              if(Math.abs(x-m) <= (Math.abs(a-m)))
              {
    
              }
             */
            double mx1 = (A.getX()+B.getX()) / 2;
            double mx2 = (C.getX()+D.getX()) / 2;
            double my1 = (A.getY()+B.getY()) / 2;
            double my2 = (C.getY()+D.getY()) / 2;
            if (Math.abs(x-mx1) <= Math.abs(A.getX()-mx1)
                    && Math.abs(x-mx2) <= Math.abs(C.getX()-mx2)
                    && Math.abs(y-my1) <= Math.abs(A.getY()-my1)
                    && Math.abs(y-my2) <= Math.abs(C.getY()-my2)) {
                flagIfFoundX = true;
                return new DataPoint(x, y);
            }
            return null;
        }
    }