arcpy – Ensuring Polyline Feature Class and LRS Network Have Same XY Resolution with ArcPy and ArcGIS Pro

arcgis-roads-highwaysarcpylinear-referencingresolution

As someone experienced in Dynamic Segmentation and Linear Referencing but new to Location Referencing within ArcGIS Roads and Highways I am struggling to use Create an LRS Network and its surrounding documentation to create my first LRS Network from a test polyline feature class which has fields that include a field identifier, from measure and to measure for each polyline in it. Creating a route feature class from this data is trivial but instead I need to create an LRS dataset and routes in an an LRS Network within that. I need the latter in order to access the route editing tools on the Location Referencing tab of the ArcGIS Pro 2.9.3 ribbon.

Below is the test code that I am using to try and create the test feature class (seems successful), an empty LRS dataset (seems successful), an empty LRS Network (seems successful) and then Append Routes into it but that last step fails with the messages and errors that follow the code:

# This script is intended to create an empty LRS dataset and load
# the geometries of a test polyline feature class with route IDs (GUID),
# Route Names (TEXT) and from/to measures (DOUBLE) into it so that an
# LRS Line Network with those routes can be created
import arcpy,uuid

def print_message(msg):
    print(msg)
    arcpy.AddMessage(msg)

arcpy.env.overwriteOutput = True

# Create empty feature class and its schema
srWGS84 = arcpy.SpatialReference("GCS_WGS_1984")
arcpy.management.CreateFileGDB(r"C:\temp","test")
gdb = r"C:\temp\test.gdb"
##arcpy.env.XYResolution = "0.0000000000000888178419700125 DecimalDegrees"
##arcpy.env.XYTolerance = "0.00000000898315284119521 DecimalDegrees"
arcpy.management.CreateFeatureclass(gdb,"testFC","POLYLINE")
testFC = r"{0}\testFC".format(gdb)
arcpy.management.DefineProjection(testFC,srWGS84)
desc = arcpy.Describe(testFC).spatialReference
print_message("testFC XY Tolerance after empty feature class creation: {0}".format(desc.XYTolerance))
print_message("testFC XY Resolution after empty feature class creation: {0}".format(desc.XYResolution))
arcpy.management.AddField(testFC,"TestFCRouteGUID","GUID")
arcpy.management.AddField(testFC,"TestFCRouteGUIDText","TEXT", None, None, 38)
arcpy.management.AddField(testFC,"TestFCRouteName","TEXT", None, None, 10)
arcpy.management.AddField(testFC,"TestFCFromMeasure","DOUBLE")
arcpy.management.AddField(testFC,"TestFCToMeasure","DOUBLE")
arcpy.management.AddField(testFC,"FromDate","DATE")
arcpy.management.AddField(testFC,"ToDate","DATE")

# Write two lines and their attributes to the feature class
cursor = arcpy.da.InsertCursor(testFC,["SHAPE@","TestFCRouteName","TestFCFromMeasure","TestFCToMeasure"])
array = arcpy.Array([arcpy.Point(0.0,0.0),
                     arcpy.Point(0.0,1.0)])
polyline = arcpy.Polyline(array,srWGS84)
cursor.insertRow([polyline,"Route1",0.0,1000.0])
array = arcpy.Array([arcpy.Point(0.0,1.0),
                     arcpy.Point(1.0,1.0)])
polyline = arcpy.Polyline(array,srWGS84)
cursor.insertRow([polyline,"Route1",1000.0,2000.0])
del cursor
desc = arcpy.Describe(testFC).spatialReference
print_message("testFC XY Tolerance after WGS84 feature insertions: {0}".format(desc.XYTolerance))
print_message("testFC XY Resolution after WGS84 feature insertions: {0}".format(desc.XYResolution))

# Provide each polyline with a GUID for its RouteID
with arcpy.da.UpdateCursor(testFC,["TestFCRouteGUID","TestFCRouteGUIDText"]) as cursor:
    for row in cursor:
        row[0] =  '{' + str(uuid.uuid4()) + '}'
        row[0] =  '{' + str(uuid.uuid4()) + '}'
        cursor.updateRow(row)

# Clean up previous test results
# Cannot rely just on arcpy.env.overwriteOutput = True because omitting
# code below leads to ERROR 130016: LRS Name: The specified name already exists
for itm in ["TestLRS","Centerline_Sequence","Lrs_Edit_Log","Lrs_Locks"]:
    if arcpy.Exists(r"{0}\{1}".format(gdb,itm)):
        print_message(r"Deleting {0}\{1}".format(gdb,itm))
        arcpy.management.Delete(r"{0}\{1}".format(gdb,itm))

# Create empty TestLRS dataset with default names for three feature classes
# (Calibration_Point,Centerline,Redline) and an LRS Hierarchy (TestLRS)
# within it, and three tables (Centerline_Sequence, Lrs_Edit_Log,Lrs_Locks)        
print_message("Creating LRS dataset named {0} in {1}".format("TestLRS",gdb))
arcpy.locref.CreateLRS(gdb,"TestLRS","Centerline","Calibration_Point",
                       "Redline","Centerline_Sequence",srWGS84,
                       "0.00000000898315284119521 DecimalDegrees",
                       "0.001 Meters",
                       "0.0001 Meters",
                       "0.0001 Meters")
centerlineFC = r"{0}\{1}\{2}".format(gdb,"TestLRS","Centerline")
desc = arcpy.Describe(r"{0}\{1}".format(gdb,"TestLRS")).spatialReference
print_message("TestLRS dataset XY Tolerance: {0}".format(desc.XYTolerance))
print_message("TestLRS dataset XY Resolution: {0}".format(desc.XYResolution))
desc = arcpy.Describe(centerlineFC).spatialReference
print_message("centerlineFC XY Tolerance: {0}".format(desc.XYTolerance))
print_message("centerlineFC XY Resolution: {0}".format(desc.XYResolution))

# Create FieldMappings object to manage merge output fields
fieldMappings = arcpy.FieldMappings()
# Add the target table to the field mappings class to set the schema
fieldMappings.addTable(centerlineFC)

# Add input fields for the RouteID field that matches the 
# target dataset
fldMap = arcpy.FieldMap()
fldMap.addInputField(testFC,"TestFCRouteGUID")
# Set name of new output field to be "RouteID"
routeGUID = fldMap.outputField
routeGUID.name, routeGUID.aliasName, routeGUID.type = "TestFCRouteGUID", "TestFCRouteGUID", "GUID"
fldMap.outputField = routeGUID
# Add output field to field mappings object
fieldMappings.addFieldMap(fldMap)

# Add input fields for the RouteID field that matches the 
# target dataset
fldMap = arcpy.FieldMap()
fldMap.addInputField(testFC,"TestFCRouteGUIDText")
# Set name of new output field to be "RouteID"
routeGUIDText = fldMap.outputField
routeGUIDText.name, routeGUIDText.aliasName, routeGUIDText.type = "TestFCRouteGUIDText", "TestFCRouteGUIDText", "TEXT"
fldMap.outputField = routeGUIDText
# Add output field to field mappings object
fieldMappings.addFieldMap(fldMap)

# Add input fields for the RouteName field that matches the 
# target dataset
fldMap = arcpy.FieldMap()
fldMap.addInputField(testFC,"TestFCRouteName")
# Set name of new output field to be "RouteID"
routeName = fldMap.outputField
routeName.name, routeName.aliasName, routeName.type = "TestFCRouteName", "TestFCRouteName", "TEXT"
fldMap.outputField = routeName
# Add output field to field mappings object
fieldMappings.addFieldMap(fldMap)

# Add input fields for the FromMeasure field that matches the 
# target dataset
fldMap = arcpy.FieldMap()
fldMap.addInputField(testFC,"TestFCFromMeasure")
# Set name of new output field to be "FromMeasure"
fromMeasure = fldMap.outputField
fromMeasure.name, fromMeasure.aliasName, fromMeasure.type = "TestFCFromMeasure", "TestFCFromMeasure", "DOUBLE"
fldMap.outputField = fromMeasure
# Add output field to field mappings object
fieldMappings.addFieldMap(fldMap)

# Add input fields for the ToMeasure field that matches the 
# target dataset
fldMap = arcpy.FieldMap()
fldMap.addInputField(testFC,"TestFCToMeasure")
# Set name of new output field to be "FromMeasure"
toMeasure = fldMap.outputField
toMeasure.name, toMeasure.aliasName, toMeasure.type = "TestFCToMeasure", "TestFCToMeasure", "DOUBLE"
fldMap.outputField = toMeasure
# Add output field to field mappings object
fieldMappings.addFieldMap(fldMap)

# Add the same fields into the Centerline feature class
arcpy.management.AddField(centerlineFC,"TestFCRouteGUID","GUID")
arcpy.management.AddField(centerlineFC,"TestFCRouteGUIDText","TEXT", None, None, 38)
arcpy.management.AddField(centerlineFC,"TestFCRouteName","TEXT", None, None, 10)
arcpy.management.AddField(centerlineFC,"TestFCFromMeasure","DOUBLE")
arcpy.management.AddField(centerlineFC,"TestFCToMeasure","DOUBLE")

# Create empty TestLRSNetwork in TestLRS dataset 
print_message("Creating LRS Network ({0}) in LRS dataset ({1})".format("TestLRSNetwork","TestLRS"))
arcpy.locref.CreateLRSNetwork(gdb, "TestLRS", "TestLRSNetwork",
                              "TestFCRouteGUID", "TestFCRouteName", "FromDate", "ToDate", "DO_NOT_DERIVE", '',
                              "DO_NOT_INCLUDE", "LineId", "LineName", "LineOrder", "METERS")
lrsNetworkFC = r"{0}\{1}\{2}".format(gdb,"TestLRS","TestLRSNetwork")
desc = arcpy.Describe(lrsNetworkFC).spatialReference
print_message("lrsNetworkFC XY Tolerance: {0}".format(desc.XYTolerance))
print_message("lrsNetworkFC XY Resolution: {0}".format(desc.XYResolution))

# Append testFC into the empty Centerline feature class
# arcpy.management.Append(testFC,centerlineFC, "NO_TEST",fieldMappings)

# Append routes from testFC into TestLRSNetwork which should append them into Centerline feature class at the same time
arcpy.locref.AppendRoutes(testFC,lrsNetworkFC,"TestFCRouteGUIDText","TestFCRouteName","FromDate","ToDate",field_map=fieldMappings)

Start Time: Friday, 24 June 2022 11:10:50 AM
testFC XY Tolerance after empty feature class creation: 8.983152841195215e-09
testFC XY Resolution after empty feature class creation: 0.0001
testFC XY Tolerance after WGS84 feature insertions: 8.983152841195215e-09
testFC XY Resolution after WGS84 feature insertions: 0.0001
Creating LRS dataset named TestLRS in C:\temp\test.gdb
TestLRS dataset XY Tolerance: 8.98315284119521e-09
TestLRS dataset XY Resolution: 8.993220460755661e-10
centerlineFC XY Tolerance: 8.98315284119521e-09
centerlineFC XY Resolution: 8.993220460755661e-10
Creating LRS Network (TestLRSNetwork) in LRS dataset (TestLRS)
lrsNetworkFC XY Tolerance: 8.98315284119521e-09
lrsNetworkFC XY Resolution: 8.993220460755661e-10
Traceback (most recent call last):
  File "\\bneprdfps24\gis\GIS\PROJECTS\GRAEME\CreateAndLoadLRSDatasetFromTestData.py", line 162, in <module>
    arcpy.locref.AppendRoutes(testFC,lrsNetworkFC,"TestFCRouteGUIDText","TestFCRouteName","FromDate","ToDate",field_map=fieldMappings)
  File "C:\Program Files\ArcGIS\Pro\Resources\ArcPy\arcpy\locref.py", line 193, in AppendRoutes
    raise e
  File "C:\Program Files\ArcGIS\Pro\Resources\ArcPy\arcpy\locref.py", line 190, in AppendRoutes
    retval = convertArcObjectToPythonObject(gp.AppendRoutes_locref(*gp_fixargs((source_routes, in_lrs_network, route_id_field, route_name_field, from_date_field, to_date_field, line_id_field, line_name_field, line_order_field, field_map, load_type), True)))
  File "C:\Program Files\ArcGIS\Pro\Resources\ArcPy\arcpy\geoprocessing\_base.py", line 512, in <lambda>
    return lambda *args: val(*gp_fixargs(args, True))
arcgisscripting.ExecuteError: ERROR 130104: XY Resolutions of testFC and TestLRSNetwork datasets are different.
Failed to execute (AppendRoutes).
Failed script Create and Load LRS Dataset from Test Data...
Failed to execute (CreateAndLoadLRSDatasetFromTestData).
Failed at Friday, 24 June 2022 11:11:50 AM (Elapsed Time: 59.96 seconds)

As you can see above I have tried to track down "ERROR 130104: XY Resolutions of testFC and TestLRSNetwork datasets are different" without success.

Is there a way that I can ensure both testFC and TestLRSNetwork have the same XY Resolution?

Best Answer

I am no longer looking for an answer to this question because at Appending Routes from Polyline Feature Class into LRS Network says duplicate route IDs with overlapping time representation found in source data I am developing and alternative workflow that guarantees the same XY Resolution and XY Tolerance for all the feature classes involved by creating my polyline feature class (which I then make "route-ready") in the feature dataset created by the Create LRS tool.

However, some advice that came from the Roads and Highways Team after I had switched to that strategy is worth recording here. They suggested:

... taking a look at the documentation topic we have about tolerance resolution settings for your LRS feature classes Tolerance and resolution settings for the LRS. The short version is that all of the minimum schema, networks, and events are interconnected so having the same XYZ tolerances/resolutions is critical. This includes your source route data used in Append Routes having the same XYZM tolerance/resolution as the LRS Network it's being loaded into. I'd verify your XYZM tolerance and resolutions between your source data and LRS Network match. If they don't, they'll need to be in alignment in order for the Append Routes tool to execute successfully.