1

I have many polylines that represent the GPS derived route of a vehicle. I have created a layer with all route segments that meet a certain wind speed threshold (not all of these line segments touch), and have made an additional field with segment grouping values that I want to dissolve segments on (segments share group values based on the time they were generated). Unfortunately, when I dissolve segments together based on group value, the new polylines don't maintain the vertex order I need, as seen below:

https://github.com/Austin-Willoughby/Projects/blob/master/example_segments.zip

Note the end vertex is on one of the middle lines; however, the vehicle was travelling West, so I need it to be on the far left segment. It seems that dissolve randomly chooses start/end line segments when creating the new vertex order. (Note, all original segments have correct vertex orders. I.e. for this image, each segment vertex ends on the Western vertex.)

I need to figure out if there is any way to control the vertex order of a dissolved polyline?

I have a time associated with each line segment that I can use to order them properly, I just am not sure how to apply this in dissolve. Context: I need a proper vertex order so that I can generate segment angle changes along the route.

Here is a link to some sample data (pre-dissolve). If you dissolve these segments by the "segment_group" field you will see the issue. https://github.com/Austin-Willoughby/Projects/blob/master/example_segments.zip

EDIT:

I've updated the sample data to include more segment group types. I've also written some code that utilizes the Array method @Hornbydd suggested and generated the segments seen below.

(Original segments are pink, generated segments are blue)

Original segments Generated segments

The newly generated lines seem to have the desired vertex order; however, the vertex coordinates seem to be off slightly. Additionally, the standalone segments (i.e. segment_group = 0 which shouldn't be combined with other lines) don't appear in the new lines. Perhaps the issue is that I try to create polylines from these single segments in the same manor as the multipart lines?

See code below:

python version 3.7.10, arcpy version 2.8

import arcpy  
import os  
from arcpy import env  

arcpy.env.workspace= os.getcwd() + "\\Wind_Speed_Analysis.gdb"
# sort feature class by segment_group then startepoch (i.e. start time) for desired segment order
arcpy.Sort_management("sample_data", "sample_data_sort", [["segment_group", "ASCENDING"], ["startepoch", "ASCENDING"]])
fc = r"sample_data_sort"
fc_len = int(arcpy.GetCount_management(fc).getOutput(0))

# Enter for loop for each feature
with arcpy.da.SearchCursor(fc, ['SHAPE@','segment_group']) as cursor:
    row_indx = 0
    segments_wind_exceedance_merged = []
    
    for row in cursor: #iterate through rows
        segment_group = row[1]
        
        # can't access prev row at line 0
        if row_indx == 0: 
            polyline = []
            prev_group = 0
            for part in row[0]:
                polyline.append(arcpy.Array(part))
            segments_wind_exceedance_merged.append(arcpy.Polyline(arcpy.Array(polyline)))
            
        # last row will always have the same segment_group directly above
        elif row_indx == fc_len-1:
            for part in row[0]:
                polyline.append(arcpy.Array(part))
            segments_wind_exceedance_merged.append(arcpy.Polyline(arcpy.Array(polyline)))
                
        # if not first row, previous row can be used
        elif row_indx > 0:
            if segment_group == 0:  # segment_group = 0 denotes standalone segments that will not be merged
                polyline = []
                prev_group = 0
                for part in row[0]:
                    polyline.append(arcpy.Array(part))
                segments_wind_exceedance_merged.append(arcpy.Polyline(arcpy.Array(polyline)))

            # segment_group = 0 are stand alone lines.  segment_group > 0 will occur in at least two rows for each group 
            elif segment_group > 0: 
                
                if segment_group != prev_group: # this will occur when loop accesses a new segment_group
                    if prev_group > 0:
                        segments_wind_exceedance_merged.append(arcpy.Polyline(arcpy.Array(polyline))) #append previous segments
                    polyline = [] # create empty polyline list for new segment
                    for part in row[0]:
                        polyline.append(arcpy.Array(part))
                    prev_group = segment_group
                    
                elif segment_group == prev_group:
                    for part in row[0]:
                        polyline.append(arcpy.Array(part))
                    prev_group = segment_group
                        
        row_indx+=1
        
        
arcpy.CopyFeatures_management(segments_wind_exceedance_merged, "Wind_Speed_Analysis.gdb\sample_polylines")
5
  • Can you share a sample of this data that exhibits the issue when you dissolve. It will be difficult for people to help if they can't replicate the problem?
    – Hornbydd
    May 24 '21 at 16:05
  • 1
    Sample data now included.
    – Austin
    May 24 '21 at 17:19
  • You have a tag for ArcPy but have not included a code snippet that illustrates what you have tried and where you are stuck.
    – PolyGeo
    May 24 '21 at 20:49
  • I had tagged it because I figured the solution was likely an ArcPy solution rather than a ArcGIS Pro one. I can untag if that's improper. I will post my code once I attempt the method @Hornbydd posted
    – Austin
    May 24 '21 at 21:27
  • Your points are off because you didn't specified spatial reference in arcpy.Polyline()
    – FelixIP
    May 27 '21 at 4:18
1

Shorter version of your script, with specified projection:

import arcpy
infc = "sample_data"
d = arcpy.Describe(infc)
SR = d.spatialReference
arcpy.AddMessage(SR.name)
GROUPS = set([int(row[0]) for row in arcpy.da.TableToNumPyArray(infc,"segment_gr")])
listOfLines = []
arr = arcpy.Array()
for item in GROUPS:
    arr.removeAll()
    q = '"segment_gr" = %i' %item
    with arcpy.da.SearchCursor(infc,"Shape@",q) as cursor:
        for row in cursor:
            part = row[0].getPart(0)
            arr.add(part)
    pline = arcpy.Polyline(arr, SR)
    listOfLines.append(pline)
outfc = "in_memory/mparts"
arcpy.management.CopyFeatures(listOfLines, outfc)

Note: tested on shapefile you provided.

2
  • Much more succinct, nice use of the set() to generate a list of unique values and the use of the whereclause in the search cursor. As dumb as it sounds I forget that exists!
    – Hornbydd
    May 27 '21 at 11:05
  • This seems to do it! I just had to change "segment_gr" to "segment_group". I don't see how you get the vertexes ordered by "startepoch" though. In my original code I used arcpy.Sort_management() to order my rows first. Perhaps the rows were already in that orientation? Also this method combines the standalone group (segment_group = 0). I can easily fix that with a filter. Now that I think about it, it was silly of me to code for that group rather than filtering out and merging later. Thank you!
    – Austin
    May 27 '21 at 13:21
0

I've looked at your sample and able to replicate the issue. I also tried the pairwise dissolve tool and weirdly it produce a different sequence. The problem can also be replicated in ArcMap.

I'm unsure why its happening so the only thing I can suggest is you use python and arcpy to build your multipart line. You can append each part to an Array object to construct the multipart.

1
  • I've attempted the Array method @Hornbydd mentioned but am having some issues. Please see code in post edit for details.
    – Austin
    May 26 '21 at 16:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.