Solved

How To Create Parallel Lines And Parallel Vertices


I am trying to create two offset lines from the original line that will be parallel and have parallel vertices from beginning to end. The Offsetter does not do this nor does the OffsetCurveGenerator, AnchoredSnapper combination do this. If I can't figure out how to do this in FME then I will perform this in Oracle Spatial. Here are the steps I will take in that case:

1.) Generate segmented lines from the original line split at each vertex point.

2.) Determine the line direction (angle) of every segment.

3.) Rotate each segment 90 degrees from each forward vertex.

4.) Extend each rotated segment so that they are long enough to apply vertices at offset distance on each side of original line..

5.) Add vertices on both sides of rotated segments at the offset distance for each side.

6.) Connect the vertices on each side of the original line to create two new parallel lines.

If there is a way to do this in FME that I have not figured out, please let me know.

Thanks

Dan

icon

Best answer by takashi 9 March 2016, 02:16

View original

12 replies

Userlevel 2
Badge +12

What is wrong with the OffsetCurveGenerator? That seems to do exactly what you describe.

What is wrong with the OffsetCurveGenerator? That seems to do exactly what you describe.

The OffsetCurveGenerator creates a curve (with multiple vertices) where the original line is angular with a single vertex. It's not a parallel line as it has a different shape. There also isn't a 1 to 1 relationship with the vertices in the original line with the offset lines if created by the Off

setCurveGenerator.
Userlevel 2
Badge +17

Hi @dastucky, summary of my idea is:

  • decompose the original line into individual segments
  • move the segments parallel
  • compute intersection between two straight lines (extensions of two adjoining segments)
  • connect the intersections to create required line

The intersection (x, y) of two straight lines can be calculated mathematically.

Line 1: (x1, y1) - (x2, y2), Line 2: (x3, y3) - (x4, y4)

Note: Line 1 is not parallel to Line 2.

a = x1 * y2 - x2 * y1

b = x3 * y4 - x4 * y3

c = (y2 - y1) * (x4 - x3) - (y4 - y3) * (x2 - x1)

x = {a * (x4 - x3) - b * (x2 - x1)} / c

y = {a * (y4 - y3) - b * (y2 - y1)} / c

Actual data flow would be a little bit complicated. See the attached example: create-parallel-line-prototype.fmw

Result:

Hope this helps.

Hi @dastucky, summary of my idea is:

  • decompose the original line into individual segments
  • move the segments parallel
  • compute intersection between two straight lines (extensions of two adjoining segments)
  • connect the intersections to create required line

The intersection (x, y) of two straight lines can be calculated mathematically.

Line 1: (x1, y1) - (x2, y2), Line 2: (x3, y3) - (x4, y4)

Note: Line 1 is not parallel to Line 2.

a = x1 * y2 - x2 * y1

b = x3 * y4 - x4 * y3

c = (y2 - y1) * (x4 - x3) - (y4 - y3) * (x2 - x1)

x = {a * (x4 - x3) - b * (x2 - x1)} / c

y = {a * (y4 - y3) - b * (y2 - y1)} / c

Actual data flow would be a little bit complicated. See the attached example: create-parallel-line-prototype.fmw

Result:

Hope this helps.

Thank you very much. Can you provide a screenshot of your workspace? I'm using ESRI Interoperability which doesn't import fmw files as far as I know.

Userlevel 2
Badge +17

Hi @dastucky, summary of my idea is:

  • decompose the original line into individual segments
  • move the segments parallel
  • compute intersection between two straight lines (extensions of two adjoining segments)
  • connect the intersections to create required line

The intersection (x, y) of two straight lines can be calculated mathematically.

Line 1: (x1, y1) - (x2, y2), Line 2: (x3, y3) - (x4, y4)

Note: Line 1 is not parallel to Line 2.

a = x1 * y2 - x2 * y1

b = x3 * y4 - x4 * y3

c = (y2 - y1) * (x4 - x3) - (y4 - y3) * (x2 - x1)

x = {a * (x4 - x3) - b * (x2 - x1)} / c

y = {a * (y4 - y3) - b * (y2 - y1)} / c

Actual data flow would be a little bit complicated. See the attached example: create-parallel-line-prototype.fmw

Result:

Hope this helps.

Hi @dastucky, see these images.

Part 1

Part 2

Part 3

Badge +3

this is also in an old post, about zero stroke offsetter...

heres an old pic (sep 2014)

Hi @dastucky, see these images.

Part 1

Part 2

Part 3

Thanks @takashi for your help. The lines look good after the implementation however I noticed that the vertices in the new line don't always line up with the vertices in the original line. For the purposes of my work, I need to have that as well in the solution I use. Is there something that can be adjusted/added to allow for this? I've attached a pic to demonstrate what I mean. The corners vertices line up well however it's the ones usually on straight lines that are off at times.

Userlevel 2
Badge +17

Hi @dastucky, summary of my idea is:

  • decompose the original line into individual segments
  • move the segments parallel
  • compute intersection between two straight lines (extensions of two adjoining segments)
  • connect the intersections to create required line

The intersection (x, y) of two straight lines can be calculated mathematically.

Line 1: (x1, y1) - (x2, y2), Line 2: (x3, y3) - (x4, y4)

Note: Line 1 is not parallel to Line 2.

a = x1 * y2 - x2 * y1

b = x3 * y4 - x4 * y3

c = (y2 - y1) * (x4 - x3) - (y4 - y3) * (x2 - x1)

x = {a * (x4 - x3) - b * (x2 - x1)} / c

y = {a * (y4 - y3) - b * (y2 - y1)} / c

Actual data flow would be a little bit complicated. See the attached example: create-parallel-line-prototype.fmw

Result:

Hope this helps.

@dastucky, when adjoining two segments are on a straight line (the vertex between them is on the straight line), "c" will be zero and the intersection won't be computed correctly. Try inserting another Tester to filter the parallel segments according to the value of "c" (whether it is near equal to zero)..

Userlevel 2
Badge +17

Hi @dastucky, I confirmed that a small Python script can perform the same manipulation. This script may also be helpful if Esri Data Interop. supports the PythonCaller.

# PythonCaller Script Example:  Offset Line
# Offset direction is determined by the sign of specified offset amount:
# Positive amount makes LEFT offset; Negative amount makes RIGHT offset.
# The offset line always will be created in 2D even if the original line is in 3D.
# Assume that the input feature has a single Line geometry,
# and the original line has no duplicate coordinates, no dangles.
# Note: Depending on the shape of the original line and the offset amount,
# there are cases where the resulting line has self-intersections. The shape of
# resulting line may not be preferable if self-intersections have occurred.

import fmeobjects, math

def offsetLine(feature):
    # Get all coordinates of the original line.
    coords = feature.getAllCoordinates()
    
    # Get offset amount from a feature attribute called "_offset".
    # Modify the attribute name if necessary.
    offset = float(feature.getAttribute('_offset'))
    
    # Calculate start and end coordinates of every parallel segment.
    segments = []
    for i in range(len(coords) - 1):
        x1, y1, x2, y2 = coords[i][0], coords[i][1], coords[i+1][0], coords[i+1][1]
        r = offset / math.hypot(x2 - x1, y2 - y1)
        vx, vy = (x2 - x1) * r, (y2 - y1) * r
        segments.append(((x1 - vy, y1 + vx), (x2 - vy, y2 + vx)))
            
    # Function that returns the intersection (x, y) of two lines.
    def intersection(((x1, y1), (x2, y2)), ((x3, y3), (x4, y4))):
        dx1, dy1, dx2, dy2 = x2 - x1, y2 - y1, x4 - x3, y4 - y3
        a, b, c = x1 * y2 - x2 * y1, x3 * y4 - x4 * y3, dy1 * dx2 - dy2 * dx1
        return ((a * dx2 - b * dx1) / c, (a * dy2 - b * dy1) / c) if 1e-12 < abs(c) else (x2, y2)
        
    # Set the resultant offset line to the input feature.
    feature.setGeometry(fmeobjects.FMELine([segments[0][0]] \
        + [intersection(s, t) for s, t in zip(segments[:-1], segments[1:])] \
        + [segments[-1][1]]))

Hi @dastucky, I confirmed that a small Python script can perform the same manipulation. This script may also be helpful if Esri Data Interop. supports the PythonCaller.

# PythonCaller Script Example:  Offset Line
# Offset direction is determined by the sign of specified offset amount:
# Positive amount makes LEFT offset; Negative amount makes RIGHT offset.
# The offset line always will be created in 2D even if the original line is in 3D.
# Assume that the input feature has a single Line geometry,
# and the original line has no duplicate coordinates, no dangles.
# Note: Depending on the shape of the original line and the offset amount,
# there are cases where the resulting line has self-intersections. The shape of
# resulting line may not be preferable if self-intersections have occurred.

import fmeobjects, math

def offsetLine(feature):
    # Get all coordinates of the original line.
    coords = feature.getAllCoordinates()
    
    # Get offset amount from a feature attribute called "_offset".
    # Modify the attribute name if necessary.
    offset = float(feature.getAttribute('_offset'))
    
    # Calculate start and end coordinates of every parallel segment.
    segments = []
    for i in range(len(coords) - 1):
        x1, y1, x2, y2 = coords[i][0], coords[i][1], coords[i+1][0], coords[i+1][1]
        r = offset / math.hypot(x2 - x1, y2 - y1)
        vx, vy = (x2 - x1) * r, (y2 - y1) * r
        segments.append(((x1 - vy, y1 + vx), (x2 - vy, y2 + vx)))
            
    # Function that returns the intersection (x, y) of two lines.
    def intersection(((x1, y1), (x2, y2)), ((x3, y3), (x4, y4))):
        dx1, dy1, dx2, dy2 = x2 - x1, y2 - y1, x4 - x3, y4 - y3
        a, b, c = x1 * y2 - x2 * y1, x3 * y4 - x4 * y3, dy1 * dx2 - dy2 * dx1
        return ((a * dx2 - b * dx1) / c, (a * dy2 - b * dy1) / c) if 1e-12 < abs(c) else (x2, y2)
        
    # Set the resultant offset line to the input feature.
    feature.setGeometry(fmeobjects.FMELine([segments[0][0]] \
        + [intersection(s, t) for s, t in zip(segments[:-1], segments[1:])] \
        + [segments[-1][1]]))

Hi @takashi.  I was able to get it to work in FME correctly last night with parallel vertices and parallel lines.  I had to play around with the "c" value to get it to provide the perfect results.  Thanks again for your help.  Also, thanks for the python script info as well!!!  I appreciate your help with this!!

Badge +3

all of this was also created in this thread

https://knowledge.safe.com/questions/3174/parallel...

and a follow up

Userlevel 4
Badge +25

The OffsetCurveGenerator creates a curve (with multiple vertices) where the original line is angular with a single vertex. It's not a parallel line as it has a different shape. There also isn't a 1 to 1 relationship with the vertices in the original line with the offset lines if created by the Off

setCurveGenerator.

Yeah, when the angle parameter is set to 90 you would expect a single angular point. That's already filed with our developers and I added this thread to see if we can increase the priority.

Reply