Question

Polygons (trapezoids) from lines


Badge

I want to make polygon (trapezoid) from each line segment that has information about trapezoid width at start and at the end.

Is it possible with FME?


12 replies

Userlevel 2
Badge +17

Hi @jernejtekavec, I guess that the line segment should be the base of the required trapezoid. Is it right? Then, not sure what the given information means, sorry. Which parts of a trapezoid does the "width at start and at the end" indicate? If you could illustrate contents of the given information using an image, it would help us to understand your requirement exactly.

Badge

Hello,

now when I read my question again, I see that I really made it confusing.

I attached a picture that will, I hope, help to understand what I'm trying to do

Userlevel 2
Badge +17

@jernejtekavec, I got it. This procedure might be a solution, applying 2D vector operations.

1. Extract start node coordinates (x0, y0) with the CoordinateExtractor.

2. Extract end node coordinates (x1, y1) with the CoordinateExtractor.

3. Calculate the length of the line segment.

4. Calculate the unit vector along the line.

ux = (x1 - x0) / length; uy = (y1 - y0) / length

5. Replace the line with the 1st vertex of the trapezoid (VertexCreator, Replace with Point mode).

X = x0 - uy * width1 * 0.5; Y = y0 + ux * width1 * 0.5

6. Add the 2nd vertex (VertexCreator, Add Point mode)

X = x0 + uy * width1 * 0.5; Y = y0 - ux * width1 * 0.5

7. Add the 3rd vertex (VertexCreator, Add Point mode)

X = x1 + uy * width2 * 0.5; Y = y1 - ux * width2 * 0.5

8. Add the 4th vertex (VertexCreator, Add Point mode)

X = x1 - uy * width2 * 0.5; Y = y1 + ux * width2 * 0.5

9. Finally transform the resulting line into a polygon with the LineCloser.

[Addition] This screenshot illustrates the workflow on the Canvas.

[Addition 2] This is another implementation of the vector operations with FME 2016. Note: in FME 2016, you can extract all coordinates as a list attribute by the CoordinateExtractor, and use an attribute value newly created in an AttributeCreator to construct other attribute values in the same AttributeCreator.

Badge +2

Hi,

Alternate approach, If I understood your required. line2polygon.fmw

Userlevel 4
Badge +25

Another version. Mine uses the 2DArcReplacer on each end point and then picks up the start/end point of each arc and joins them all together.

trapezoidreplacer.fmw

Userlevel 2
Badge +17

Interesting. There is more than one way, almost always :-)

0684Q00000ArLTcQAN.png

You can also use a HullAccumulator to create the trapezoid, by connecting it to the Rotators in the above workflow.

Then, Python version for the 2D vector operation approach.

# PythonCaller Script Example
# Replace a line segment with a trapezoid polygon.
# Assuming that the input feature has a single line segment geometry.
import fmeobjects, math

def replaceLineSegmentWithTrapezoid(feature):
    # Get coordinates of the start and end nodes.
    coords = feature.getAllCoordinates()
    x0, y0 , x1, y1 = coords[0][0], coords[0][1], coords[1][0], coords[1][1]
    
    # Compute the unit vector along the original line segment
    len = math.hypot(x1 - x0, y1 - y0)
    ux, uy = (x1 - x0) / len, (y1 - y0) / len
    
    # Create a line that represents the boundary of required trapezoid.
    w1 = float(feature.getAttribute('width1')) * 0.5
    w2 = float(feature.getAttribute('width2')) * 0.5
    if feature.getDimension() == fmeobjects.FME_TWO_D: # 2D
        coords = [(x0 - uy * w1, y0 + ux * w1),  (x0 + uy * w1, y0 - ux * w1), \
            (x1 + uy * w2, y1 - ux * w2),  (x1 - uy * w2, y1 + ux * w2)]
    else: # 3D
        z0, z1 = coords[0][2], coords[1][2]
        coords = [(x0 - uy * w1, y0 + ux * w1, z0),  (x0 + uy * w1, y0 - ux * w1, z0), \
            (x1 + uy * w2, y1 - ux * w2, z1),  (x1 - uy * w2, y1 + ux * w2, z1)]
    boundary = fmeobjects.FMELine(coords) # create a line from 4 vertices.
    boundary.appendPoint(coords[0]) # close the line.
    
    # Replace geometry of the input feature with a trapezoid polygon.
    feature.setGeometry(fmeobjects.FMEPolygon(boundary))
Userlevel 4
Badge +25

Just a thought, but I suspect the main problem you might find in all these solutions - including mine - is that we tested against a single line. Once you get multiple lines involved you'll need to look at giving them an ID and setting some group-by parameters.

Badge +3

lot of lines...

Just offset the centrepoint of each line by half the width to each side.

Then hullaccumulate it.

Userlevel 4
Badge +13

Wow, I'm impressed. Many many cool ways to skin the cat. I leave it to @jernejtekavec to pick the one he likes best as the "best answer". I love them all!

Userlevel 2
Badge +17

Yes, many ways. I have six ways so far. This is the latest one:

I noticed that math expressions cannot be evaluated in the conditional value setting for the OffsetCurveGenerator's Offset parameter. The AttributeCreator is a meantime workaround.

It would be better if the HullAccumulator had "Input is Ordered by Group" option, for similar scenarios to this question.

[Addition] The tenth. Tried the new "Polar Coordinate" mode for the Offsetter of FME 2016.

Badge +3

@jernejtekavec

Less is better...

..right part is all it takes.

Affiners are same, could be stuffed in a custom. (i was too lazy to do that now).

So 3 transformers is enough...

Badge +3

as i was playing anyway..

here is with pointoffset..lowfootprint

Use azimuth for the angle (or your own one-liner..;))

Reply