PostGIS Linestrings – Creating Fixed Angle and Length Attached to a Point

linestringpointpostgispostgresqlspatial-database

I want to create linestrings with a definite angle (e.g. 160°) and length (e.g. 2m) that are fixed to a number of points of another linestring. So, I want to use the ST_DumpPoints function to find the points and bind the created linestrings to them.
Is there a way to declare an angle (α) during the linestring creation?
Here is an example picture:

enter image description here

I want to create the blue linestrings.

EDIT

The angle (α2) in the picture isn't really exemplary. But rather an azimuth of 160° (like α1).

UPDATE

The answer from Evil Genius helped me calculating the maximum width of a polygon with a given aspect.

Best Answer

You could accomplish this a few different ways depending on what sort of output you are wanting, but the concept is the same. It's generally easier to do a simple rotation followed by a translation rather than trying to calculate the coordinates in a single step.

In this case, the basic steps are:

  • Create a line of the desired length at the origin (0,0). This line should run along the axis that you wish to measure your angle from and have it's center at the origin.
  • Rotate the line around the origin.
  • Translate the line by the coordinates of the point that you want it to be centered on.

The following PostGIS view creates the lines from your example scenario. A few things are assumed:

  • The geometry column is called shape
  • The angle is measured from the x axis. Your example drawing was a bit confusing since you first mentioned 40 degrees, drew a dotted vertical line, but then said it should be around 160 degrees. I've interpreted that to mean you actually want to measure from the x axis.
  • The data is projected in the same units that you want to measure with (ie. meters).

    CREATE OR REPLACE VIEW <viewname> AS
    WITH vertices AS
    (SELECT 
          objectid, 
          (ST_DumpPoints(shape)).path[1] AS v_id, 
          (ST_DumpPoints(shape)).geom AS vertex
    FROM <source_data>
    )
    SELECT
        objectid,
        v_id,
        ST_SetSRID(ST_Translate(ST_Rotate(ST_MakeLine(ST_MakePoint( 1.0,0.0), 
                                                      ST_MakePoint(-1.0,0.0)),
                                          radians(40)), ST_X(vertex), ST_Y(Vertex)),
                                          ST_SRID(vertex)) AS newline
    FROM vertices
    

To break down what's actually going on with that last line, starting from the inner most: ST_SetSRID(ST_Translate(ST_Rotate(ST_MakeLine(ST_MakePoint( 0,1.0), ST_MakePoint(0,-1.0)), radians(40)), ST_X(vertex), ST_Y(Vertex)), ST_SRID(vertex)) AS newline

  • ST_MakePoint(1.0,0.0) and ST_MakePoint(-1.0,0.0): Create the endpoints for a horizontal line that is our desired length and centered on the origin.
  • ST_MakeLine(...): Use our newly created end points to create a line.
  • ST_Rotate(..., radians(40)): Rotate that new line around the origin.
  • ST_Translate(..., ST_X(vertex), ST_Y(vertex)): Center the rotated line onto our reference (input) point.
  • ST_SetSRID(..., ST_SRID(vertex)): Give the new line the same SRID as the input geometry.

If you are using PostGIS 2.0 you can simplify this since you can specify a different origin for ST_Rotate. If you want to rotate to an angle based on the slope of the line, you'll have to calculate that first and add it to the rotation angle.

If the data isn't projected in the same units that you want to measure in you can still do something similar, but you'll need an extra step:

  • Create a line (projected in something that uses what you want to measure in)
  • Rotate
  • Reproject to your target projection
  • Translate to the target point

Edit

I now understand what you mean by the angle. Essentially, you want a rotation clockwise from the Y axis (0 is up, 90 is right, 180 is down, etc.).

You do still need to use the radians function since ST_Rotate expect the angle in radians. You should be able to get the correct angle with two small changes:

  • Start with a vertical line (use ST_MakePoint(0.0,1.0) and ST_MakePoint(0.0,-1.0))
  • Multiply your angle by -1. This will make it negative, causing ST_rotate to rotate it in a clockwise direction. radians(<angle> * -1)
Related Question