Search code examples
geometryleafletqgis

How to get the 4 coordinates of a rectangle from 3 coordinates?


I want to create a function that allows user to draw rectangle from 3 points (blue points):

  • The first two points will form an edge (width).
  • The third point will determine the height.

I need this custom draw function in leaflet, however leaflet's default rectangle is created with 2 diagonal points: https://leafletjs.com/reference-1.5.0.html#rectangle.

I need to calculate one of the green points but my small brain can't seem to figure it out :P

PS/EDIT: The rectangle might be angled, this is what makes it challenging

enter image description here


Solution

  • Leaflet's default rectangle is created with 2 diagonal points

    [...]

    The rectangle might be angled, this is what makes it challenging

    Be aware that Leaflet's L.Rectangle is created from a L.LatLngBounds, a bounding box in which the edges are implicitly aligned to the coordinate grid. Don't use bounding boxes, and rely on L.Polygon instead, providing all four points.


    Let A and B be the points of the base of the rectangle, and C be the point on the top. Assuming all points are Javascript structures of the form {x: Number, y: Number}, and assuming that you're working in an euclidean plane (i.e. not on the surface of a geoid),

    First, calculate the distance from a point to a line defined by the other two points, i.e. the distance from C to the line defined by AB. Let that be distance (note that it's equal to "height" in your diagram):

    var distance = Math.abs( 
        (A.y - B.y) * C.x  -  (A.x - B-x) * C.y  +  B.x * A.y  -  B.y * A.x ) 
      ) / Math.sqrt(
        Math.pow(B.y - A.y, 2) + Math.pow(B.x - A.x, 2)
      );
    

    Then, let AB be the vector from A to B

    var AB = {  x: B.x - A.x,   y: B.y - A.y };
    

    (Note that the length of AB is equal to "width" in your diagram)

    Calculate a unit vector perpendicular to AB:

    var perpendicular = {x: -AB.y, y: AB.x}
    var perpendicularSize = Math.sqrt(AB.x * AB.x + AB.y * AB.y);
    var unit = {x: perpendicular.x / perpendicularSize, y: perpendicular.y / perpendicularSize};
    

    Multiply that unit vector by the distance from C to AB to get the vectors for the "sides" of your rectangle:

    var sideVector = { x: unit.x * distance, y: unit.y * distance };
    

    ...and create new points D and E by offsetting A and B by the vector for the sides of the rectangle:

    var D = { x: A.x + sideVector.x, y: A.y + sideVector.y };
    var E = { x: B.x + sideVector.x, y: B.y + sideVector.y };
    

    ...And your rectangle is now defined by the points ABDE. Note that C is in the line defined by points DE.