Search code examples
javacoordinateslatitude-longitudegeogeotools

Perpendicular Point Placement


I have a Line that has a Start and End coordinate on the earth.
I'm trying to place perpendicular points on each side of the Start point length distance away. enter image description here

Originally I thought I could

  • Get the Slope of the line
  • Determine the slope for the perpendicular Line at the start point
  • Solve for x and y
Coordinate p1 = Ppoint(start, end, length); 
Coordinate p2 = Ppoint(start, end, -(length)); 

public static Coordinate Ppoint(Coordinate start, Coordinate end, double length){
   double slope = getSlope(start, end);
   double pSlope;  
   if(slope != 0)
   {
      pSlope = -(1/slope); 
   } 
   else 
   { 
      pSlope = 0; 
   }

   double b = start.y + (-(pSlope * start.x)); 

   double x = (start.x + length);
   double y = (pSlope * x) + b; 

   Return new Coordinate(x,y); 
}

I think there's a problem with doing math on lat/lon and accounting for their ranges
and this doesn't account for the earth not being flat.
Is there better way to approach this ?


Solution

  • You should probably not attempt to do this sort of maths on a sphere (while it can be made to work, it is hard and slow).

    Assuming that length is of the order of 10s-100s of kilometres you should reproject your problem to a "flat" surface centred on the start point and use Euclidean maths on a plane.

    Fortunately, GeoTools provides handy automatic projections for just this problem. Here x & y are the coordinate of the start point (lon==x, lat==y):

    String code = "AUTO:42001," + y + "," + x;
    // System.out.println(code);
    CoordinateReferenceSystem auto = CRS.decode(code);
    // System.out.println(auto);
    MathTransform transform = CRS.findMathTransform(DefaultGeographicCRS.WGS84,
        auto);
    MathTransform rTransform = CRS.findMathTransform(auto, DefaultGeographicCRS.WGS84);
    

    You can then use the transform object to convert your points to the new projection:

    Geometry g3 = JTS.transform(g1, transform);
    

    do whatever maths you need and then transform back to lat, lon using rTransform

    So to adapt this to your problem.

    Coordinate start = new Coordinate(1.0, 51.0);
    Coordinate end = new Coordinate(2.0, 52.0);
    double length = 10000;
    GeometryFactory gf = new GeometryFactory();
    
    double x = start.getX();
    double y = start.getY();
    String code;
    if(CRS.getAxisOrder(DefaultGeographicCRS.WGS84).equals(AxisOrder.EAST_NORTH)) {
      code = "AUTO:42001," + x + "," + y;
    } else {
      code = "AUTO:42001," + y + "," + x;
    }
    CoordinateReferenceSystem auto = CRS.decode(code);
    MathTransform transform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, auto);
    MathTransform rTransform = CRS.findMathTransform(auto, DefaultGeographicCRS.WGS84);
    
    Point pStart = gf.createPoint(start);
    Point pEnd = gf.createPoint(end);
    
    Point ptStart = (Point) JTS.transform(pStart, transform);
    Point ptEnd = (Point) JTS.transform(pEnd, transform);
    
    Coordinate p1 = pPoint(ptStart.getCoordinate(), ptEnd.getCoordinate(), length);
    
    Point tPoint = gf.createPoint(p1);
    Point p = (Point) JTS.transform(tPoint, rTransform);
    System.out.println(p);
    

    which gives me POINT (1.2643 47.6531) which looks wrong to me! You may need to check the maths in the pPoint method.