AnsweredAssumed Answered

Export only dominant routes from LRS?

Question asked by vitale232 on Jan 28, 2019
Latest reply on May 5, 2020 by Kyle.gonterwitz_KSDOT

I'm looking to export the entirety of our enterprise LRS out of ArcMap, but with a caveat. When there is a concurrency, I would like only the dominant route present in the exported data.

 

I've got a workflow that seems to give me what I want, but it's a lot of steps.

 

Here's what I'm currently using:

  1. Run the Calculate Route Concurrencies GP tool with the appropriate temporal view date
  2. Export all non-retired routes into a separate LRS feature class
  3. Select the dominant routes from the output table, and run the Make Route Event GP tool from the Location Referencing toolbox to create an event of dominant routes
  4. Use the Erase GP tool from the Analysis toolbox to remove all concurrent sections from the LRS feature class
  5. Merge the dominant events with the LRS feature class that's output from Erase
  6. Dissolve on Route ID

 

Does anyone have a simpler workflow to accomplish this? Do you see any flaws in this current workflow?

 

Thanks in advance.

 

Edit (1/30/2018):

I'm including a Python script that implements something similar to Amit Hazra's model builder screenshot. Maybe someone will find it useful. Thanks for the tips, RHUG.

 

Edit (3/17/2020)

I ended up turning this into an ArcGIS Desktop Geoprocessing Tool as well. It's only been tested on ArcGIS Desktop 10.5.1 with an LRSN Route Network stored in a file geodatabase or SQL Server 2016 geodatabase. If you're using a different RDBMS, the where_clause that applies the Temporal View Date may be different.

GitHub - vitale232/ExportDominantNetwork: Export dominant routes from an Esri R&H ALRS. 

I've also attached a .zip directory containing the tool as of today's date.

import datetime
import os

import arcpy


def make_dominant_network(input_routes, concurrency_table, overwrite_flag=False):
    """
    After creating gaps in the input network where concurrencies exist,
    the geometries must be split to single parts. Then use the m-values
    from the single part geometries to create new routes. This is done to avoid
    non-monotonic routes. Non-monotonicity can be introduced from multipart
    outputs in GP tools, which are likely to reorder the part indices
    """

    dom_table_path  = os.path.join(
        os.path.dirname(input_routes),
        'dominant_table'
    )
    dom_events_path = os.path.join(
        os.path.dirname(input_routes),
        'dominant_event'
    )
    erased_routes_path = os.path.join(
        os.path.dirname(input_routes),
        'milepoint_concurr_gaps'
    )
    erased_routes_singlepart_path = os.path.join(
        os.path.dirname(input_routes),
        'milepoint_concurr_gaps_singlepart'
    )
    merged_routes_path = os.path.join(
        os.path.dirname(input_routes),
        'milepoint_dom_event_merge'
    )
    output_path = os.path.join(
        os.path.dirname(input_routes),
        'milepoint_dominant_network'
    )

    if overwrite_flag and arcpy.Exists(dom_table_path):
        print(' overwrite deleting {}'.format(dom_table_path))
        arcpy.Delete_management(dom_table_path)
    if overwrite_flag and arcpy.Exists(dom_events_path):
        print(' overwrite deleting {}'.format(dom_events_path))
        arcpy.Delete_management(dom_events_path)
    if overwrite_flag and arcpy.Exists(erased_routes_path):
        print(' overwrite deleting {}'.format(erased_routes_path))
        arcpy.Delete_management(erased_routes_path)
    if overwrite_flag and arcpy.Exists(merged_routes_path):
        print(' overwrite deleting {}'.format(merged_routes_path))
        arcpy.Delete_management(merged_routes_path)
    if overwrite_flag and arcpy.Exists(output_path):
        print(' overwrite deleting {}'.format(output_path))
        arcpy.Delete_management(output_path)

    if not overwrite_flag and arcpy.Exists(merged_routes_path):
        field_names = [field.name for field in arcpy.ListFields(merged_routes_path)]
        if all(['m_min' in field_names, 'm_max' in field_names]):
            add_m_fields = False
        else:
            add_m_fields = True

    # Begin geoprocessing logic
    if not arcpy.Exists(dom_table_path):
        print('\nSubsetting concurrency table to only dominant events')
        where_clause = "(DominantFlag = 1) AND (DominantError <> 4)"
        print(' {}'.format(where_clause))
        arcpy.TableToTable_conversion(
            concurrency_table,
            os.path.dirname(dom_table_path),
            os.path.basename(dom_table_path),
            where_clause=where_clause
        )

    if not arcpy.Exists(dom_events_path):
        print('\nCreating event for route dominance')
        line_props = 'RouteId LINE FromMeasure ToMeasure'
        dom_layer = arcpy.MakeRouteEventLayer_lr(
            input_routes, 'ROUTE_ID', dom_table_path, line_props,
            'dom_layer'
        )
        arcpy.CopyFeatures_management(
            dom_layer, dom_events_path
        )
        print(' {}'.format(dom_events_path))

    if not arcpy.Exists(erased_routes_path):
        print('\nCreating network gaps at concurrencies')
        arcpy.Erase_analysis(input_routes, dom_events_path, erased_routes_path)
        print(' {}'.format(erased_routes_path))

    if not arcpy.Exists(erased_routes_singlepart_path):
        print('\nSplitting gapped network to single-part geometries')
        arcpy.MultipartToSinglepart_management(
            erased_routes_path, erased_routes_singlepart_path
        )
        print(' {}'.format(erased_routes_singlepart_path))


    if not arcpy.Exists(merged_routes_path):
        print('\nMerging dominant routes with gapped network')
        field_mapping = map_fields(
            [erased_routes_singlepart_path, 'ROUTE_ID'],
            [dom_events_path, 'RouteId'],
            'ROUTE_ID'
        )
        arcpy.Merge_management(
            [erased_routes_singlepart_path, dom_events_path],
            merged_routes_path,
            field_mapping
        )
        print(' {}'.format(merged_routes_path))
        add_m_fields = True

    if add_m_fields:
        print('\nAdding m-values to the merged routes attribute table')
        merged_field_names = [
            field.name for field in arcpy.ListFields(merged_routes_path)
        ]
        if not 'm_min' in merged_field_names:
            arcpy.AddField_management(
                merged_routes_path,
                'm_min',
                'DOUBLE',
            )
            print(' created field: m_min')
        if not 'm_max' in merged_field_names:
            arcpy.AddField_management(
                merged_routes_path,
                'm_max',
                'DOUBLE',
            )
            print(' created field: m_max')
        update_fields = ['SHAPE@', 'm_min', 'm_max']
        with arcpy.da.UpdateCursor(merged_routes_path, update_fields) as update_cursor:
            for row in update_cursor:
                shape = row[0]
                m_min = shape.extent.MMin
                m_max = shape.extent.MMax
                update_cursor.updateRow([shape, m_min, m_max])
        print(' table update: complete')
   
    if not arcpy.Exists(output_path):
        print('\nCreating final network of only dominant routes')
        arcpy.CreateRoutes_lr(
            merged_routes_path, 'ROUTE_ID', output_path,
            measure_source='TWO_FIELDS',
            from_measure_field='m_min', to_measure_field='m_max',
            build_index='INDEX'
        )
        print(' {}'.format(output_path))


def map_fields(table_field_a, table_field_b, output_name):
    field_mappings = arcpy.FieldMappings()

    field_map = arcpy.FieldMap()
    field_map.addInputField(*table_field_a)
    field_map.addInputField(*table_field_b)
    output_field = field_map.outputField
    output_field.name = output_name
    field_map.outputField = output_field
    field_mappings.addFieldMap(field_map)

    return field_mappings


def main():
    input_routes =      r'D:\Pavement\Input_Data\Milepoint.gdb\Milepoint_20190116'
    concurrency_table = r'D:\Pavement\Input_Data\Milepoint.gdb\route_concurrencies_tvd20190116'
    overwrite_flag =    False

    make_dominant_network(input_routes, concurrency_table, overwrite_flag=overwrite_flag)


if __name__ == '__main__':
    start_time = datetime.datetime.now()
    print('Running script: {0}\nStart time: {1}'.format(
        os.path.abspath(__file__), start_time)
    )

    main()

    end_time = datetime.datetime.now()
    print('\nCompleted at: {}.\nTime to complete: {}'.format(
        end_time, end_time - start_time)
    )

Outcomes