Question

I need to find a bounding box given 4 lat/long points and a bearing (as seen in the example picture). I always know which two points are lined up by the bearing (1 and 2 in the example), so I will always know the length of the bounding box. The width however is arbitrary, with the points being anywhere along the lines (3 and 4 in the example).

enter image description here

My first thought is that I'll have to calculate the angles between the points (1 & 3, 1 & 4, 2 & 3, 2 & 4) and then use a series of "law of cosine" equations to calculate the corner points. Is there a simpler way? Would that even work?

Was it helpful?

Solution

So looking around and even asking on probably a more appropriate site (here), I found a solution based on something by Chris Veness (here) to find the intersection point given two points and their bearings. So to get the corners of the bounding box I just take each combination of top/bottom and left/right (1 & 3, 1 & 4, 2 & 3, 2 & 4) and find the intersections using the known bearing and adjusting accordingly. For example to find the bottom right of the image I'd calculate the intersection of points 1 & 3 using the bearing + 90 for the direction of point 1 and the bearing - 180 for the direction of point 3.

I can take no credit for the algorithm or even really explain it in terms of how it works geometrically, but its worked in my testing. Below is my java translation from the javascript version provided by Chris

public static CoordD getIntersection(CoordD point1, double bearing1, CoordD point2, double bearning2) {
    double lat1 = rad(point1.latitude); double lon1 = rad(point1.longitude);
    double lat2 = rad(point2.latitude); double lon2 = rad(point2.longitude);
    double bearing13 = rad(bearing1); double bearing 23 = rad(bearing2);
    double dLat = lat2 - lat1; double dLon = lon2 - lon1;

    double dist12 = 2 * Math.asin( Math.sqrt( Math.sin(dLat / 2) * Math.sin(dLat / 2) +
        Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2) * Math.sin(dLon / 2) ) );
    if (dist12 == 0) return null;

    double bearingA = Math.acos( ( Math.sin(lat2) - Math.sin(lat1) * Math.cos(dist12) ) /
        ( Math.sin(dist12) * Math.cos(lat1) ) );
    double bearingB = Math.acos( ( Math.sin(lat1) - Math.sin(lat2) * Math.cos(dist12) ) /
        ( Math.sin(dist12) * Math.cos(lat2) ) );
    if (Double.isNaN(bearingA)) bearingA = 0;
    if (Double.isNaN(bearingB)) bearingB = 0;

    double bearing12, bearing21;
    if (Math.sin(dLon) > 0) {
        bearing12 = bearingA;
        bearing21 = 2 * Math.PI - bearingB;
    } else { 
        bearing12 = 2 * Math.PI - bearingA;
        bearing21 = bearingB;
    }

    double alpha1 = (bearing13 - bearing12 + Math.PI) % (2 * Math.PI) - Math.PI; // Angle 2-1-3
    double alpha2 = (bearing21 - bearing23 + Math.PI) % (2 * Math.PI) - Math.PI; // Angle 1-2-3

    if (Math.sin(alpha1) == 0 && Math.sin(alpha2) == 0) return null; // Infinite intersections
    if (Math.sin(alpha1) * Math.sin(alpha2) < 0) return null; // Ambiguous intersection

    // needed?
    // alpha1 = Math.abs(alpha1);
    // alpha2 = Math.abs(alpha2);

    double alpha3 = Math.acos( -Math.cos(alpha1) * Math.cos(alpha2) +
        Math.sin(alpha1) * Math.sin(alpha2) * Math.cos(dist12) );
    double dist13 = Math.atan2( Math.sin(dist12) * Math.sin(alpha1) * Math.sin(alpha2),
        Math.cos(alpha2) + Math.cos(alpha1) * Math.cos(alpha3) );

    double lat3 = Math.asin( Math.sin(lat1) * Math.cos(dist13) +
        Math.cos(lat1) * Math.sin(dist13) * Math.cos(bearing13) );

    double dLon13 = Math.atan2( Math.sin(bearing13) * Math.sin(dist13) * Math.cos(lat1),
        Math.cos(dist13) - Math.sin(lat1) * Math.sin(lat3) );
    double lon3 = lon1 + dLon3;
    lon3 = (lon3 + 3 * Math.PI) % ( 2* Math.PI) - Math.PI // normalize to +/-180

    return new CoordD(deg(lat3), deg(lon3));
}

rad() and deg() are just helper functions that translate between radians and degrees. CoordD is a helper class that just contains two double to store a lat/long point.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top