图中两个系列/折线图的交点

时间:2018-08-16 09:21:52

标签: android graph charts mpandroidchart android-graphview

我想在图形的x-y平面上标记两条线的交点。

在我的应用程序中使用GraphView,我以以下方式绘制了2个系列:

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);

更新:- 没有两个数据点相等。

如何用标签标记图中的两条线/系列的交点?

是否可以使用GraphView / MPAndroidChart或任何其他库?

任何答复/评论/对解决方案的参考将不胜感激。

我想实现类似于此图所示的内容: enter image description here

此处的交叉点用红点标记并带有标签。

2 个答案:

答案 0 :(得分:1)

您的问题更多是关于如何找到两个图形/系列之间的交点的算法问题。在所附的代码中,我将您的两个系列更改为LineGraphSeries,并创建了第三个PointGraphSeries系列3。

要创建第三个系列,我使用了一种简单的算法,首先找到两个系列的交点,然后将迹线添加到图形中。

GraphView graph = findViewById(R.id.graph);

DataPoint[] points1 = new DataPoint[]{
        new DataPoint(3, 4),
        new DataPoint(4, 4.1),
        new DataPoint(5, 4.2),
        new DataPoint(6, 4.3),
        new DataPoint(7, 4.4),
        new DataPoint(8, 4.4),
        new DataPoint(9, 4.4),
        new DataPoint(10, 4.4),
        new DataPoint(11, 4.5),
        new DataPoint(12, 4.5),
        new DataPoint(13, 4.3),
        new DataPoint(14, 4.4),
        new DataPoint(15, 4.5),
        new DataPoint(16, 4.6),
        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(31, 6.0),
        new DataPoint(32, 6.0)
};

DataPoint[] points2 = 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)
};

LineGraphSeries<DataPoint> series1 = new LineGraphSeries<>(points1);
series1.setColor(Color.BLACK);
graph.addSeries(series1);


LineGraphSeries<DataPoint> series2 = new LineGraphSeries<>(points2);
series2.setColor(Color.MAGENTA);
graph.addSeries(series2);

// Third graph that is based on the intersections
PointsGraphSeries<DataPoint> series3 = new PointsGraphSeries<>();
series3.setShape(PointsGraphSeries.Shape.POINT);
series3.setColor(Color.RED);

for (DataPoint point1 : points1) {
    for (DataPoint point2 : points2) {
        if (point1.getX() == point2.getX()
                && point1.getY() == point2.getY()) {
            series3.appendData(point1, true, points1.length);
        }

    }
}
graph.addSeries(series3);

enter image description here

答案 1 :(得分:0)

谢谢@svahidhoss的帖子。它帮助我找到了解决问题的方法。

我使用了您的示例,并在没有DataPoints相等的情况下添加了if条件。

下面是解决方法:

            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;
    }
}