4

I'm making a application to track a veicle based in GPS coordinates.

I created a SurfaceView to draw the field, vehicle and the path (route) for him.

The result looked like this:

enter image description here

The black dots represent the coming of GPS coordinates, and blue rectangles would be the area covered by the path traveled. (the width of the path is configurable)

The way I'm drawing with blue rectangles (this is my question) which are the area covered by the path traveled. (the width of the path is configurable)

With that I need to overcome some situation.

  • I need to calculate the field's rotation angle so that the path always get left behind. (completed)
  • I need to calculate the angle of rotation of each rectangle so they are facing towards the vehicle. (completed)

In the future I will need:

  • Detect when the vehicle passes twice in the same place. (based on the path traveled)
  • Calculate the area (m²) all traveled by the vehicle.

I would like some tips for draw this path.

My code:

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

My goal is to have something like this application: https://play.google.com/store/apps/details?id=hu.zbertok.machineryguide (but only in 2D for now)

EDIT:

To clarify a bit more my doubts:

  • I do not have much experience about it. I would like a better way to draw the path. With rectangles it was not very good. Note that the curves are some empty spaces.
  • Another point is the rotation of rectangles, I'm rotating them at the time of drawing. I believe this will make it difficult to detect overlaps
  • I believe I need math help for the rotation of objects and overlapping detection. And it also helps to draw the path of a filled shape.
  • That path looks already fine. It is unclear to me where and how you need help. With the mathematics, algorithms or programming the drawings. – greenapps Jun 26 '15 at 06:40
  • Thanks for commenting. I do not have much experience about it. I would like a better way to draw the path. With rectangles it was not very good. Note that the curves are some empty spaces.... Another point is the rotation of rectangles, I'm rotating them at the time of drawing. I believe this will make it difficult to detect overlaps.... I believe I need math help for the rotation of objects and overlapping detection. And it also helps to draw the path of a filled shape. – Douglas Nassif Roma Junior Jun 26 '15 at 14:15

2 Answers2

1

After some research time, I came to a successful outcome. I will comment on my thoughts and how was the solution.

As I explained in question, along the way I have the coordinates traveled by the vehicle, and also a setting for the width of the path should be drawn.

Using LibGDX library is ready a number of features, such as the implementation of a "orthographic camera" to work with positioning, rotation, etc.

With LibGDX I converted GPS coordinates on my side points to the road traveled. Like this: enter image description here

The next challenge was to fill the path traveled. First I tried using rectangles, but the result was as shown in my question.

So the solution was to trace triangles using the side of the path as vertices. Like this: enter image description here

Then simply fill in the triangles. Like this: enter image description here

Finally, using Stencil, I set up OpenGL to highlight overlaps. Like this: enter image description here

Other issues fixed:

  • To calculate the covered area, simply calculate the area of existing triangles along the path.
  • To detect overlapping, just check if the current position of the vehicle is within a triangle.

Thanks to:

  • AlexWien for the attention and for their time.
  • Conner Anderson by videos of LibGDX
  • And a special thanks to Luis Eduardo for knowledge, helped me a lot.

The sample source code.

0

Usually such a path is drawn using a "path" method from the graphics lib. In that lib you can create a polyline, and give a line width. You further specify how corners are filled. (BEVEL_JOIN, MITTER_JOIN)

The main question is wheter the path is drawn while driving or afterwords. Afterwords is no problem. To draw while driving might be a bit tricky to avoid to redraw the path each second.

When using the Path with moveTo and lineTo to create a polyline, then you can set a line width, and the graphics lib will do that all for you. Then there will be no gaps, since it is a poly line.

AlexWien
  • 28,470
  • 6
  • 53
  • 83
  • Hi @AlexWien, thank you for answer... I've tried to work with circles, and really have problems between large intervals... To answer your question, the drawing is done in real time as the vehicle is moving... The graphics library referred, would be OpenGL? – Douglas Nassif Roma Junior Jun 26 '15 at 19:45
  • For ios developers, there is a breadcrumbs demo app. This shows the technique using apple graphic lib. And it shows the trick to avoid redrawing the parts of the path which are unchanged, by overriding the drawRect() method. I uses ios quarz as graphic lib. For android you must find out what they use. OpenGL is used on ios only for "games" where you need full control of the whole screen. I asume that for android it is the same, there should be a standard graphic libs, where you can draw rectanges and circles. – AlexWien Jun 29 '15 at 17:50
  • Android there are two situations: Canvas or OpenGL. In my question I'm using Canvas... But my problem is not the fact of "redraw" the screen. My problem is with the techniques used to perform the operations: draw route, detect overlapping, etc. – Douglas Nassif Roma Junior Jun 29 '15 at 18:43
  • As already told, there is a class called "Path" were you have to call path.MoveTo(_points[0].X, _points[0].Y) and path.LineTo. Then you have to set a line width. (see also http://forums.xamarin.com/discussion/4635/how-to-draw-a-polyline-polygon-on-existing-view) – AlexWien Jun 29 '15 at 20:58
  • Yes, but with the line I can not detect overlap. That is, when is going through a road already traveled, in real time. Or am I wrong? – Douglas Nassif Roma Junior Jun 29 '15 at 23:12
  • An overlap you detect by mathematics (vectors, polygons, distances, geo transformation to cartesian space, etc.) , usually not by graphics. You never explained that you want to detect a road already traveled. Please read your question again, and if neccessary formulate a new question. – AlexWien Jun 30 '15 at 15:32
  • Sorry if I was not very clear on the question. But I mentioned about "overlap", "path traveled" and "passes twice in the same place" =/ ... I need to detect this overlap and still paint a different color. Then need to calculate the area based on these coordinates ... So the correct way would paint the lines and try to calculate the overlap with math? I thought the visual library would help me in this regard. – Douglas Nassif Roma Junior Jun 30 '15 at 16:42
  • That depends what you will do with the detected overlap. Do you justr want to draw the overlap on the screen, or do you want to caluclate the area of overlap and display the number of overlapped square meters. You must go more in detail. Graphic lib can draw the: overlapped in different color using xor color mask mode: mentioed here: http://stackoverflow.com/questions/12127187/what-does-graphics-setxormodecolor-do-in-simple-terms – AlexWien Jun 30 '15 at 17:51