16

Please refer to the example and corresponding image.

I would like to achieve the following: provide two locations (lat/lng), which are shown below as A and B. From this, a virtual line would be drawn and then the distance between this line and C would be calculated (in any measurement).

drawing

I have achieved this currently in Google Maps API v3 but would want to also be able to perform this behind the scenes in my language of choice. Any tips/ideas would be greatly appreciated!

8
  • Is AB a Great Circle line? Commented Jun 23, 2011 at 16:05
  • @Kirk, No, AB is just a straight line
    – Prisoner
    Commented Jun 23, 2011 at 16:14
  • @Michael, thats an interesting point. I will have to have a look into it!
    – Prisoner
    Commented Jun 23, 2011 at 16:15
  • @Prisoner @Kirk Literally, a "straight line" will pass beneath the earth's surface. In general, its radial projection back onto the surface will indeed be a segment of a great circle (using a spherical earth model).
    – whuber
    Commented Jun 23, 2011 at 16:26
  • 1
    @Prisoner That is an extremely useful extra piece of information! Yes, you are correct. You still have to compensate for the fact that using (lat,lon) differentially distorts east-west distances compared to north-south. As @Jose advises, project the coordinates. This can be as simple as pre-multiplying the longitudes by the cosine of the average latitude and then pretending you're on a Euclidean plane.
    – whuber
    Commented Jun 23, 2011 at 16:32

4 Answers 4

11

Maybe I'm making it too complicated, but what you want is the distance from a point to a line. That is the distance from a point along AB that links AB with C with a line orthogonal to AB. This vector perpendicular to AB is given by

v=[x2-x1, -(y2-y1)] # Point A is [x1,y1] Point B is [x2,y2]

(I have used the square brackets to define a vector, or a two-element array). The distance between C [xp, yp] and point A is

u=[x1-xp, y1-xp]

The distance between the line and C is just the projection of u on to v. If we assume mod(v) = 1 (just normalise it), then

distance = u*v = abs( (x2-x1)*(y1-yp) - (x1-xp)*(y2-y1) )

The only complication is that you probably want to make sure your coordinates are not WGS84 lat/log pairs, but projected (or use geodetic coordinates). You can use OGR or Proj4 for this.

8
  • 3
    +several million pseudo-points for not using trigonometric functions, by the way. All too many people pull out ArcTan when they should be looking at this: en.wikipedia.org/wiki/Dot_product
    – Herb
    Commented Jun 23, 2011 at 16:11
  • @Jose, thanks for the reply! I'm using the lat/long from google maps API. The maths part is quite new to me so I'll give it a go and see what I can come up with. Any tips with the maths? E.g. [x2-x1, -(y2-y1)], what does that translate to?
    – Prisoner
    Commented Jun 23, 2011 at 16:21
  • I have added a short edit for this. Basically, it's an array notation, but if you store your coordinates in variables x1, x2, y1, y2 and xp, yp, you only need to write the right hand side of the last equation I've provided. This is pretty much valid C, Java, JS, Python etc code :)
    – Jose
    Commented Jun 23, 2011 at 16:26
  • 2
    @Jose You are computing the distance from C to the line AB. Based on the figure, I believe the OP wants the distance from C to the line segment AB. This requires extra work to check whether the projection of C onto line AB lies between A and B or not. In the latter case, use the shorter of the two lengths CA and CB.
    – whuber
    Commented Jun 23, 2011 at 16:29
  • 2
    @Prisoner The main difference is that a line extends forever (it is only defined by a direction vector and a point, or by two points), whereas a segment between A and B is the bit of the the infinite line that goes between A and B (it has a finite length)
    – Jose
    Commented Jun 23, 2011 at 17:25
7
def get_perp( X1, Y1, X2, Y2, X3, Y3):
    """************************************************************************************************ 
    Purpose - X1,Y1,X2,Y2 = Two points representing the ends of the line segment
              X3,Y3 = The offset point 
    'Returns - X4,Y4 = Returns the Point on the line perpendicular to the offset or None if no such
                        point exists
    '************************************************************************************************ """
    XX = X2 - X1 
    YY = Y2 - Y1 
    ShortestLength = ((XX * (X3 - X1)) + (YY * (Y3 - Y1))) / ((XX * XX) + (YY * YY)) 
    X4 = X1 + XX * ShortestLength 
    Y4 = Y1 + YY * ShortestLength
    if X4 < X2 and X4 > X1 and Y4 < Y2 and Y4 > Y1:
        return X4,Y4
    return None

The shortest length is the distance you require, unless I am mistaken?

19
  • Yes, I am looking for the shortest distance from C to the line segment. Is this what this math computes?
    – Prisoner
    Commented Jun 24, 2011 at 9:30
  • 1
    It did indeed work fine, I passed the three points (A,B,C) in the following: i.imgur.com/bK9oB.jpg and it came back with the lat/lng of X. Great work!
    – Prisoner
    Commented Jun 24, 2011 at 11:59
  • 1
    @Hairy, One last thing, how would I go about modifying this to go to the nearest point (not just line) so if I had put it past the point of joing a line, how could I get it to check the distance to the point?
    – Prisoner
    Commented Jun 24, 2011 at 14:47
  • 1
    @Hairy Good start, but it seems that too often this code returns None when a legitimate solution exists. The problem is that the last conditional assumes X1 < X2 and Y1 < Y2, which cannot always be assured. A better test of betweenness is needed.
    – whuber
    Commented Jun 26, 2011 at 18:11
  • 1
    @Hairy It sounds like this exchange between you and @prisoner has been productive. I would like to emphasize that I have had nothing to do with (or even any control over) any changes in voting or points that may have occurred and that my comment was intended only to help you improve your reply.
    – whuber
    Commented Jun 27, 2011 at 13:18
4

Being a bit averse to all this math as well, I would come at it from a different angle. I would make it an 'actual' line, rather than a virtual line, and then use existing tools.

If A and B share an attribute, you could connect them by drawing a line (Kosmo GIS has a tool that will create lines from points, and I believe there is also a QGIS plugin for this). Once you have the lines, a 'near' function on the 'C' point layer will give you the distance to the line. Let the software handle the math for you!

2
  • Thanks for the comment but Hairy came up trumps on this one!
    – Prisoner
    Commented Jun 24, 2011 at 12:01
  • 1
    (+1) You make an excellent point. Computational geometry algorithms are notoriously tricky to get completely right in practice (as we can see from all the code offered so far, which is helpful and illustrative but doesn't yet fully work). Using a high-level GIS procedure often is a good way to assure you are getting the answer you expect and that it's right (provided you trust your GIS ;-).
    – whuber
    Commented Jun 27, 2011 at 13:22
1

If you were using java on android, its only one line with the library function

import static com.google.maps.android.PolyUtil.distanceToLine;

distanceToLine:

public static double distanceToLine(LatLng p, LatLng start,LatLng end)

Computes the distance on the sphere between the point p and the line segment start to end.

Parameters: p - the point to be measured

start - the beginning of the line segment

end - the end of the line segment

Returns: the distance in meters (assuming spherical earth)

Just add library to your

dependencies {
    compile 'com.google.maps.android:android-maps-utils:0.5+'
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.