【问题标题】:Best way to draw a path traveled绘制路径的最佳方法
【发布时间】:2015-07-08 03:32:50
【问题描述】:

我正在制作一个基于 GPS 坐标跟踪车辆的应用程序。

我创建了一个 SurfaceView 来为他绘制场地、车辆和路径(路线)。

结果如下所示:

黑点代表 GPS 坐标的到来,蓝色矩形是路径所覆盖的区域。 (路径宽度可配置)

我用蓝色矩形(这是我的问题)绘制的方式是路径所覆盖的区域。 (路径宽度可配置)

因此我需要克服一些情况。

  • 我需要计算场的旋转角度,这样路径总是被抛在后面。 (已完成)
  • 我需要计算每个矩形的旋转角度,使它们面向车辆。 (已完成)

将来我需要:

  • 检测车辆何时在同一地点两次通过。 (基于经过的路径)
  • 计算车辆行驶的面积 (m²)。

我想要一些绘制这条路径的提示。

我的代码:

public void draw(Canvas canvas) {
    Log.d(getClass().getSimpleName(), "draw");
    canvas.save();

    // translate canvas to vehicle positon
    canvas.translate((float) center.cartesian(0), (float) center.cartesian(1));

    float fieldRotation = 0;

    if (trackerHistory.size() > 1) {
         /*
        Before drawing the way, only takes the last position and finds the angle of rotation of the field.
         */
        Vector lastPosition = new Vector(convertToTerrainCoordinates(lastPoint));
        Vector preLastPosition = new Vector(convertToTerrainCoordinates(preLastPoint));
        float shift = (float) lastPosition.distanceTo(preLastPosition);

        /*
        Having the last coordinate as a triangle, 'preLastCoord' saves the values of the legs, while 'shift' is the hypotenuse
        */
        // If the Y offset is negative, then the opposite side is the Y displacement
        if (preLastPosition.cartesian(1) < 0) {
            // dividing the opposite side by hipetenusa, we have the sine of the angle that must be rotated.
            double sin = preLastPosition.cartesian(1) / shift;

            // when Y is negative, it is necessary to add or subtract 90 degrees depending on the value of X
            // The "Math.asin()" calculates the radian arc to the sine previously calculated.
            // And the "Math.toDegress()" converts degrees to radians from 0 to 360.
            if (preLastPosition.cartesian(0) < 0) {
                fieldRotation = (float) (Math.toDegrees(Math.asin(sin)) - 90d);
            } else {
                fieldRotation = (float) (Math.abs(Math.toDegrees(Math.asin(sin))) + 90d);
            }
        }
        // if not, the opposite side is the X offset
        else {
            // dividing the opposite side by hipetenusa have the sine of the angle that must be rotated.
            double senAngulo = preLastPosition.cartesian(0) / shift;

            // The "Math.asin()" calculates the radian arc to the sine previously calculated.
            // And the "Math.toDegress()" converts degrees to radians from 0 to 360.
            fieldRotation = (float) Math.toDegrees(Math.asin(senAngulo));
        }
    }

    final float dpiTrackerWidth = Navigator.meterToDpi(trackerWidth); // width of rect

    final Path positionHistory = new Path(); // to draw the route
    final Path circle = new Path(); // to draw the positions

    /*
    Iterate the historical positions and draw the path
    */
    for (int i = 1; i < trackerHistory.size(); i++) {
        Vector currentPosition = new Vector(convertToTerrainCoordinates(trackerHistory.get(i))); // vector with X and Y position
        Vector lastPosition = new Vector(convertToTerrainCoordinates(trackerHistory.get(i - 1))); // vector with X and Y position

        circle.addCircle((float) currentPosition.cartesian(0), (float) currentPosition.cartesian(1), 3, Path.Direction.CW);
        circle.addCircle((float) lastPosition.cartesian(0), (float) lastPosition.cartesian(1), 3, Path.Direction.CW);

        if (isInsideOfScreen(currentPosition.cartesian(0), currentPosition.cartesian(1)) ||
                isInsideOfScreen(lastPosition.cartesian(0), lastPosition.cartesian(1))) {
            /*
            Calcule degree by triangle sides
             */
            float shift = (float) currentPosition.distanceTo(lastPosition);
            Vector dif = lastPosition.minus(currentPosition);
            float sin = (float) (dif.cartesian(0) / shift);

            float degress = (float) Math.toDegrees(Math.asin(sin));

            /*
            Create a Rect to draw displacement between two coordinates
             */
            RectF rect = new RectF();
            rect.left = (float) (currentPosition.cartesian(0) - (dpiTrackerWidth / 2));
            rect.right = rect.left + dpiTrackerWidth;
            rect.top = (float) currentPosition.cartesian(1);
            rect.bottom = rect.top - shift;

            Path p = new Path();
            Matrix m = new Matrix();
            p.addRect(rect, Path.Direction.CCW);
            m.postRotate(-degress, (float) currentPosition.cartesian(0), (float) currentPosition.cartesian(1));
            p.transform(m);

            positionHistory.addPath(p);
        }
    }

    // rotates the map to make the route down.
    canvas.rotate(fieldRotation);

    canvas.drawPath(positionHistory, paint);
    canvas.drawPath(circle, paint2);

    canvas.restore();
}

我的目标是有这样的应用程序:https://play.google.com/store/apps/details?id=hu.zbertok.machineryguide(但现在只有 2D)

编辑:

澄清一下我的疑惑:

  • 我对此没有太多经验。我想要一种更好的方法来绘制路径。对于矩形,它不是很好。请注意,曲线是一些空白区域。
  • 另一点是矩形的旋转,我在绘制的时候旋转它们。我相信这会使检测重叠变得困难
  • 我相信我需要数学帮助来处理对象的旋转和重叠检测。它还有助于绘制填充形状的路径。

【问题讨论】:

  • 这条路径看起来已经很好了。我不清楚你在哪里以及如何需要帮助。用数学、算法或编程绘图。
  • 感谢您的评论。我对此没有太多经验。我想要一种更好的方法来绘制路径。对于矩形,它不是很好。请注意,曲线是一些空白区域......另一点是矩形的旋转,我在绘制时旋转它们。我相信这会使检测重叠变得困难......我相信我需要数学帮助来进行对象的旋转和重叠检测。它还有助于绘制填充形状的路径。

标签: android canvas gps trigonometry


【解决方案1】:

经过一段时间的研究,我得出了一个成功的结果。我将评论我的想法以及解决方案如何。

正如我在问题中解释的那样,沿途我有车辆行驶的坐标,还应该绘制路径宽度的设置。

使用LibGDX 库准备了许多功能,例如实现“正交相机”来处理定位、旋转等。

使用 LibGDX,我将侧点上的 GPS 坐标转换为行进的道路。像这样:

下一个挑战是填补走过的路。首先我尝试使用矩形,但结果如我的问题所示。

所以解决方案是使用路径的一侧作为顶点来跟踪三角形。像这样:

然后简单地填充三角形。像这样:

最后,我使用 Stencil 设置 OpenGL 以突出显示重叠。像这样:

已修复的其他问题:

  • 要计算覆盖面积,只需计算沿路径现有三角形的面积即可。
  • 要检测重叠,只需检查车辆的当前位置是否在三角形内。

感谢:

  • AlexWien 的关注和他们的时间。
  • 康纳·安德森videos of LibGDX
  • 特别感谢 Luis Eduardo 提供的知识,对我帮助很大。

sample source code

【讨论】:

  • 美丽。但这真的可以在 Android 上运行吗?
  • 很棒的工作!你有什么想法如何为 Windows CE 做同样的事情吗?谢谢
【解决方案2】:

通常使用图形库中的“路径”方法绘制这样的路径。 在该库中,您可以创建一条折线,并给出线宽。 您进一步指定如何填充角落。 (BEVEL_JOIN, MITTER_JOIN)

主要问题是路径是在驾驶时绘制的还是在后记时绘制的。 后记是没有问题的。 在驾驶时绘制可能有点棘手,要避免每秒重新绘制路径。

当使用带有 moveTo 和 lineTo 的路径来创建折线时,您可以设置线宽,图形库会为您完成这一切。 那么就不会有空隙了,因为它是一条折线。

【讨论】:

  • 嗨@AlexWien,谢谢你的回答......我尝试过使用圆圈,并且在大间隔之间确实存在问题......为了回答你的问题,绘图是实时完成的随着车辆的移动......所引用的图形库是OpenGL吗?
  • 对于 ios 开发人员,有一个面包屑演示应用程序。这显示了使用苹果图形库的技术。它显示了通过覆盖 drawRect() 方法来避免重绘路径中未更改的部分的技巧。我使用 ios quarz 作为图形库。对于 android 你必须找出他们使用什么。 OpenGL 仅在 ios 上用于需要完全控制整个屏幕的“游戏”。我假设对于android来说也是一样的,应该有一个标准的图形库,你可以在其中绘制矩形和圆形。
  • Android 有两种情况:Canvas 或 OpenGL。在我的问题中,我正在使用 Canvas ......但我的问题不是“重绘”屏幕的事实。我的问题在于用于执行操作的技术:绘制路线、检测重叠等。
  • 如前所述,如果您必须调用 path.MoveTo(_points[0].X, _points[0].Y) 和 path.LineTo,则有一个名为“Path”的类。然后你必须设置一个线宽。 (另见forums.xamarin.com/discussion/4635/…
  • 是的,但是用这条线我无法检测到重叠。也就是说,何时通过已经走过的道路,实时。还是我错了?
猜你喜欢
  • 2014-12-25
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多