Lect0r / osmdroid

Automatically exported from code.google.com/p/osmdroid
0 stars 0 forks source link

Improvement - path overlay #36

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
Hi,

I implemented a path overlay, for drawing GPS traces.

Made some modifications in OpenStreetMapViewProjection, to get better
performance on large traces.

Original issue reported on code.google.com by viesturz on 7 Apr 2010 at 11:50

Attachments:

GoogleCodeExporter commented 9 years ago

Original comment by neilboyd on 13 Apr 2010 at 9:16

GoogleCodeExporter commented 9 years ago
This issue was closed by revision r128.

Original comment by neilboyd on 13 Apr 2010 at 9:18

GoogleCodeExporter commented 9 years ago
Hi,

I'm just trying to implement the onSingleTap method to recognize if a line has 
been 
touched using your posted code.

I've slightly rewritten your concept, so it is structured more like 
OpenStreetMapItemizedOverlay. This means I'm having one Object 
OpenStreetMapViewOverlayPath which contains all information for one path. Then 
I add 
a list of OpenStreetMapViewOverlayPath objects to OpenStreetMapViewPathOverlay 
which 
is then added to OpenStreetMapView.mOverlays. This is working for me.

But I'm struggling with the projections to recognize if a line has been touched 
by 
the user.

Here's my current onSingleTab:

[code]
@Override
    public boolean onSingleTapUp(MotionEvent event,
            OpenStreetMapView mapView)
    {
        final OpenStreetMapViewProjection pj = mapView.getProjection();
        final int eventX = (int)event.getX();
        final int eventY = (int)event.getY();

        Point mCurScreenCoordsLinePoint1 = new Point();
        Point mCurScreenCoordsLinePoint2 = new Point();
        Point mTabCoords = new Point();

        for(int i = 0; i < this.mItemList.size(); i++)
        {
            final OpenStreetMapViewOverlayPath mItem = 
this.mItemList.get(i);

            for(int n = 0; n < mItem.mPoints.size(); n++)
            {
                if(n+1 < mItem.mPoints.size())
                {
                    // line point 1
                    pj.toMapPixels(new 
GeoPoint(mItemList.get(i).mPoints.get(n).y, 

mItemList.get(i).mPoints.get(n).x)
                                , 
mCurScreenCoordsLinePoint1);
                    // line point 2
                    pj.toMapPixels(new 
GeoPoint(mItemList.get(i).mPoints.get(n+1).y, 

mItemList.get(i).mPoints.get(n+1).x)
                                , 
mCurScreenCoordsLinePoint2);
                    // my tap position
                    mTabCoords = pj.fromPixels(eventX, eventY, 
OpenStreetMap.isSlided);

if(isPointOnLine(mCurScreenCoordsLinePoint1.x, mCurScreenCoordsLinePoint1.y, 

mCurScreenCoordsLinePoint2.x, mCurScreenCoordsLinePoint2.y, 
                                    mTabCoords.x, 
mTabCoords.y))
                    {
                        if(onTap(i))
                            return true;
                    }
                }
            }
        }

        return super.onSingleTapUp(event, mapView);
    }

    public static boolean isPointOnLine(float lox, float loy, float ltx, float 
lty, float x, float y) 
    { 
        //determine if point is on line 
        Float dx = x - lox; 
        Float dy = y - loy; 
        Float tx = ltx - lox; 
        Float ty = lty - loy; 

        //normalise the line vector 
        Float t1 = new Float(1/Math.sqrt(tx*tx+ty*ty));

        tx *= t1; 
        ty *= t1; 

        //calculate inverse length of secondary vector 
        Float dl = new Float(1/Math.sqrt(dx*dx+dy*dy)); 

        //take dot product of normalised line vector, and rotated normalised 
secondary vector 
        Float dot = (dy*tx-dx*ty)*dl; 
        //Increase these values for less or more picky 
        if (dot < -0.2 || dot > 0.2) 
           return false; 

        //calculate distance along line segment by taking dot product of normalised 
line vector and un-normalised secondary vector 
        Float dis = tx*dx+ty*dy; 
        if (dis < 0 || dis > 1 / t1)
           return false; 

        return true; 
    }
[/code]

Currently I'm using the same type of projection as used in 
OpenStreetMapViewItemizedOverlay - I don't think that's correct, is it?

When I have the starting and endpoint of a subline in an overall line, I check 
if it 
has been touched using the PointOnLine algorithm.

When it's possible to recognize a correct touch event I would love to add some 
kind 
of bubble as in OpenStreetMapViewItemizedOverlayWithFocus. But right now I'm 
really 
struggling with the projection stuff.

I attached my two classes.

Thank you in advance and thanks for the great path overlay!

Original comment by d.sc...@gmail.com on 29 Apr 2010 at 3:10

GoogleCodeExporter commented 9 years ago
I'm sorry, I forgot to attach the code. Here it is now.

Original comment by d.sc...@gmail.com on 29 Apr 2010 at 3:12

Attachments:

GoogleCodeExporter commented 9 years ago
Hi!

I will implement a hit testing method, that takes care of the projections.

Original comment by viesturz on 30 Apr 2010 at 7:19

GoogleCodeExporter commented 9 years ago
Made a simple patch that demoes hit testing in path overlay.

Note that the code tests only for distance to points, not distance to lines.

Original comment by viesturz on 30 Apr 2010 at 10:08

Attachments:

GoogleCodeExporter commented 9 years ago
wow, thanks viesturz! I'm going to test it as soon as possible. But problems 
could 
occur when the starting and end point of a line are farther from eachother 
away, am I 
right?

cheers,
d.schre

Original comment by d.sc...@gmail.com on 30 Apr 2010 at 11:07

GoogleCodeExporter commented 9 years ago
Here is a patch that tests line segments.

Original comment by viesturz on 30 Apr 2010 at 12:29

Attachments:

GoogleCodeExporter commented 9 years ago
thank you very much viesturz!

I'm trying to understand the algorithm you implemented.
This part in findPointInPath confuses me a little bit:

// call in onSingleTap
findPointInPath(canvasPxels.x, canvasPxels.y, Math.max(event.getSize(), 
this.mPaint.getStrokeWidth()) + 5, mapView.getProjection());

// code in findPointInPath
float approxDist = Math.max(Math.abs(pt.x - testX), Math.abs(pt.y - testY)) - 
Math.abs(pt.x - prevX) - Math.abs(pt.y - prevY);

if (approxDist <= maxDistance) // <-- Couldn't be there a problem if the line 
segment's points are too far away from eachother?

As I am understanding it right now, the start and end point of a line segment 
can not 
be farther away from eachother than the size of the touch event (finger) or the 
StrokeWidth + 5 (pixels) to be recognized as touched? Is my assumption correct?

Because it's working really good if you touch the line near it's points but it 
gets 
tricky when you try to touch a (very) long line in the middle.

Thanks!

Original comment by d.sc...@gmail.com on 5 May 2010 at 12:34

GoogleCodeExporter commented 9 years ago
Hi!

Yes your assumption is correct. I tried to make some approximation of the touch 
size
as max from touch size and line width, added the 5 units, just in case. Feel 
free to
use some other values.

// code in findPointInPath
float approxDist = Math.max(Math.abs(pt.x - testX), Math.abs(pt.y - testY)) - 
Math.abs(pt.x - prevX) - Math.abs(pt.y - prevY);

A very rough approximation to test if the line is far away from the point. Try 
to
follow me here carefully:
The current point is at least Math.max(Math.abs(pt.x - testX), Math.abs(pt.y -
testY)) from test point. And the previous point is no more than Math.abs(pt.x -
prevX) + Math.abs(pt.y - prevY) closer to test point than the current point 
(imagine
it being in middle between test point and current point). Thus the test point 
is at
least (the whole expr.) away from any point on the line segment.

You can try to replace the *if (approxDist <= maxDistance)* with *if (true)* 
and test
if it makes the code work better in your case.

Viesturs

Original comment by viesturz on 5 May 2010 at 12:49

GoogleCodeExporter commented 9 years ago
Ok, thanks for the explanation! I just increased maxDist to make it work for 
longer 
lines.

Original comment by d.sc...@gmail.com on 7 May 2010 at 7:40

GoogleCodeExporter commented 9 years ago
Just a note:

maxDist is maximum distance from the line, regardless of the length of the line.

Original comment by viesturz on 7 May 2010 at 8:02

GoogleCodeExporter commented 9 years ago
Is this finished now? What patch should I apply to the source?

Original comment by neilboyd on 11 May 2010 at 7:20

GoogleCodeExporter commented 9 years ago
It's not quite ready for the source, just a implementation of a hit testing 
function.
There is still a problem of defining a nice API.

d.schre has his own path overlay implementation that supports multiple paths.

I suggest we make a generic item view overlay that can house both POIs and 
paths.

Original comment by viesturz on 11 May 2010 at 11:28

GoogleCodeExporter commented 9 years ago
Perhaps I should remove the patch I applied for revision r128 then? And wait 
until it's 
finished.

Original comment by neilboyd on 11 May 2010 at 11:36

GoogleCodeExporter commented 9 years ago
Nope, that patch is fine. But it contains only the basic functionality for 
displaying
tracks. The later patches are for interacting with the tracks.

Perhaps we should move it to another issue.

Original comment by viesturz on 11 May 2010 at 12:15

GoogleCodeExporter commented 9 years ago
It's a bit late now ;-)

Original comment by neilboyd on 11 May 2010 at 2:54

GoogleCodeExporter commented 9 years ago
Not really, just 6 pm at UTC+02 ;)

Original comment by viesturz on 11 May 2010 at 2:56

GoogleCodeExporter commented 9 years ago

Original comment by neilboyd on 14 Oct 2010 at 6:58