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

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

结果如下所示:

黑点代表 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 中)

    编辑:

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

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

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

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

    使用 LibGDX,我将我身边的 GPS 坐标转换为行驶的道路。像这样:

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

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

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

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

    其他问题已修复:

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

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

  • sample source code

    关于android - 绘制路径的最佳方法,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31056755/

    10-11 22:30
    查看更多