Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calculation of perpendicular point of a line (p1, p2) and a point (p3) near the line #2

Open
phranck opened this issue Oct 3, 2018 · 7 comments

Comments

@phranck
Copy link

phranck commented Oct 3, 2018

Hey @florianreinhart!

First: Thank you very much for this great collection of useful geodesy functions!

This isn't a real issue but more a question. I'm currently struggling on calculating the perpendicular point of a line (p1, p2) and a point (p3) near the line.

Background: We are using MKPolyLine to draw lines on the map. The user should be able to tap (p3) on (or near to) a line segment (p1, p2). Now I need the closest point in the line (the perpendicular point) to the touch point, to show a popup with detailed informations at this line point. It should look like this:

screen shot 2018-10-03 at 17 42 24

Now I'm asking myself if this is possible with your functions? Maybe I'm missing something, unfortunately I don't find a solution...

Question: Is this calculation possible with your library or is an entire new function needed to get the result?

Thanks in advance!

@florianreinhart
Copy link
Owner

Hi @phranck,

This should be possible with func alongTrackDistance(toPath path: (start: Coordinate, end: Coordinate)) -> Distance.

However, This function returns the distance from start on your path. You could then calculate the initial bearing for your path and travel the calculated distance with this bearing from your start point to get a point on your path. Some sample code:

let p1, p2, p3: Coordinate

let distance = p3.alongTrackDistance(toPath: (start: p1, end: p2))
let bearing = p1.bearing(to: p2)
let pointOnPath = destination(with: distance, bearing: bearing)

@phranck
Copy link
Author

phranck commented Oct 4, 2018

@florianreinhart Yeah, that was my initial thought, too. However, this is more an approximation method than a direct calculation. (-;

@florianreinhart
Copy link
Owner

It should be quite accurate on a sphere (which is the model that is used for the calculation).

You could try doing the coordinate calculation with MKMapPoints and calculate the "Lotfußpunkt".

@phranck
Copy link
Author

phranck commented Oct 4, 2018

Yeah, using MKMapPoint's was my first attempt. Unfortunately, MapKit fails when it comes to calculation around the anti meridian. It can't deal with that and provides one with weird results!
Anyways, I think I'll go with the approximation method. (-;

@phranck
Copy link
Author

phranck commented Oct 4, 2018

should be quite accurate on a sphere

This is key! So, while handling with MKMapPoint's which represents points on a flattened earth with parallel longitudes I think I can't use your functions, can I? 🤔

@florianreinhart
Copy link
Owner

Oh yeah! You need to convert your MKMapPoints to CLLocationCoordinate2D. The Coordinate type of this library is equivalent to CLLocationCoordinate2D.

@phranck
Copy link
Author

phranck commented Oct 5, 2018

Just to illustrate - This is the code I use for getting the perpendicular point. As mentioned, it works very well, but for lines crossing -180/180.

    private func closestPointInLine(withStartPoint p1: MKMapPoint, endPoint p2: MKMapPoint, touchPoint pt: MKMapPoint, allowedPerimeter: Double) -> MKMapPoint? {
        let lineLength = distance(from: p1, to: p2)

        var u = ((pt.x - p1.x) * (p2.x - p1.x)) + ((pt.y - p1.y) * (p2.y - p1.y))
        u = u / pow(lineLength, 2)

        if u >= 0 && u <= 1 {
            let px = p1.x + u * (p2.x - p1.x)
            let py = p1.y + u * (p2.y - p1.y)

            let p = MKMapPoint(x: px, y: py)
            let touchDistance = distance(from: pt, to: p)
            if touchDistance <= allowedPerimeter {
                return p
            }
        }

        return nil
    }

    private func distance(from p1: MKMapPoint, to p2: MKMapPoint) -> Double {
        return sqrt(pow(p2.x - p1.x, 2) + pow(p2.y - p1.y, 2))
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants