Set analysis inputs

One of the most important parts of performing network analysis with the arcpy.nax module's solver objects is loading inputs. This topic describes how to properly set your analysis inputs.

Inputs can be loaded using the following:

  • The solver object's load method can load an existing feature class, table or layer.
  • The solver object's insertCursor method can insert data.

Note:

This topic uses Service Area analysis as an example; however, the information provided here can be applied to any type of network analysis.

Load inputs with the load method

If your inputs are already in a feature class, table, layer, feature set, or record set, the quickest way to use them in an analysis is with the load method. The load method adds the contents of your existing input to the network analysis class.

The following code snippet shows how to load facilities from a feature class using the load method when performing a service area analysis.


input_facilities = "C:/data/io.gdb/FireStations"
service_area.load(arcpy.nax.ServiceAreaInputDataType.Facilities, input_facilities)

Learn more about the load method for Service Area analysis in the Methods section of the ServiceArea solver object's documentation.

Use field mappings to preserve fields from your input data

Each network analysis class has a particular schema, and the values of each input field may be important for your analysis. However, the names of the fields supported by the network analysis input class might not match the names of the fields in your input dataset. Use the fieldMappings method to handle the difference. Use fieldMappings to associate a field in your input data to the supported property in the network analysis input class.

For example, consider a Service Area analysis used to determine the area reachable within a 10-minute response time from a set of fire stations. Although most of the 10 minutes represents the time spent driving from the fire station to the site of the emergency, the fire crew must spend time putting on protective equipment and preparing their truck before they can leave the station and start driving. The Service Area solver's input Facilities class has a field named AdditionalTime that can be used to model this necessary extra preparation time.

Your input data may also have a field that represents additional time, but the field might have a different name, such as TurnoutTime. You can use field mapping to map your TurnoutTime field to the AdditionalTime field. When you run the load method with this data, the AdditionalTime field in the Facilities input class will be populated with the values from your TurnoutTime field.

The fieldMappings method generates an NAClassFieldMappings dictionary in which the keys are the supported property names for the network analysis class (such as AdditionalTime) and the values are NAClassFieldMap objects that can be used to set the desired mapping behavior. You can map your TurnoutTime field to the AdditionalTime property by retrieving the AdditionalTime property from the dictionary and setting the associated NAClassFieldMap object's mappedFieldName property to the TurnoutTime field.

You can also set a default value using the defaultValue property. This value will be used for any facility (fire station, in this example) where the TurnoutTime field is null. Note that you can set the defaultValue property for any field even if you are not setting the mappedFieldName to match a field in your data.

The following code snippet shows how to load Service Area facilities using field mapping to map values from a field in your data into a supported property of the Service Area Facilities class.


input_facilities = "C:/data/io.gdb/FireStations"

# Construct a field mapping object
field_mappings = service_area.fieldMappings(arcpy.nax.ServiceAreaInputDataType.Facilities)

# Map the TurnoutTime field from your input data to the Service Area Facilities
# class's AdditionalTime property
field_mappings["AdditionalTime"].mappedFieldName = "TurnoutTime"

# Set a default of one minute of additional time in case the TurnoutTime field
# has a null value
field_mappings["AdditionalTime"].defaultValue = 1

# Load your input data using the field mappings
service_area.load(
    arcpy.nax.ServiceAreaInputDataType.Facilities,
    input_facilities,
    field_mappings
)

Learn more about the fieldMappings method for Service Area analysis in the Methods section of the ServiceArea solver object's documentation.

See a complete list of fields supported by each input class for a Service Area analysis

Insert inputs using the insertCursor method

If your inputs are something other than a feature class or layer, such as a .csv file with latitude and longitude or the results of some other analysis, you can directly insert these records into your network analysis using the insertCursor method. This method isn't the same as arcpy.da.InsertCursor, but it behaves in a very similar way.

When you create the cursor, specify which supported input fields you want to set values for using the field_names parameter. Once you have created the cursor, insert rows one at a time with the cursor object's insertRow method. For most network analysis input classes, you will need to specify the geometry of the inputs by using one of the supported shape tokens. For example, using the SHAPE@ token allows you to pass inputs using a geometry object. You can use the SHAPE@XY token to pass a tuple of X and Y coordinates in the spatial reference of the network data source used for the analysis.

The following code snippet shows how to insert Service Area facilities using the insertCursor method. This example uses the SHAPE@XY token to insert the locations of the facilities using their latitude and longitude.


# Define fields to use with the insertCursor
fields = ["Name", "AdditionalTime", "SHAPE@XY"]

# Construct the insertCursor for the desired input type using the fields
# specified
with service_area.insertCursor(arcpy.nax.ServiceAreaInputDataType.Facilities, fields) as cur:
    # Insert rows. The lists of values inserted match the designated fields.
    cur.insertRow(["Fire Station 1", 1, (-117.10191118199998, 32.634351493000054)])
    cur.insertRow(["Fire Station 2", 1, (-116.97970607599996, 32.56210221400005)])
    cur.insertRow(["Fire Station 3", 2.5, (-116.97141447099995, 32.654230331000065)])
    cur.insertRow(["Fire Station 4", 1.5, (-117.00762504, 32.70097640100005)])

The following code snippet shows how to insert Service Area facilities using the insertCursor method. This example constructs point geometries from latitude and longitude values and inserts them using the SHAPE@ token.


sr_wgs84 = arcpy.SpatialReference(4326)

# Input data with latitude and longitude values specified in WGS84 coordinates
input_data = [
    ["Fire Station 1", 1, -117.10191118199998, 32.634351493000054],
    ["Fire Station 2", 1, -116.97970607599996, 32.56210221400005],
    ["Fire Station 3", 2.5, -116.97141447099995, 32.654230331000065],
    ["Fire Station 4", 1.5, -117.00762504, 32.70097640100005]
]

# Define fields to use with the insertCursor
fields = ["Name", "AdditionalTime", "SHAPE@"]

# Construct the insertCursor for the desired input type using the fields
# specified
with service_area.insertCursor(arcpy.nax.ServiceAreaInputDataType.Facilities, fields) as cur:
    for input_pt in input_data:
        # Construct a PointGeometry object for the point using the
        # correct spatial reference
        pt_geom = arcpy.PointGeometry(
            arcpy.Point(input_pt[2], input_pt[3]), sr_wgs84)

        # Insert the data using its shape
        cur.insertRow([input_pt[0], input_pt[1], pt_geom])

The following code snippet shows how to insert Service Area facilities using the insertCursor method. This example inserts rows from a .csv file.

The code assumes that the data stored in the .csv file includes the facility name, additional time, longitude, and latitude, similar to the list in the previous example.


# Define fields to use with the insertCursor
fields = ["Name", "AdditionalTime", "SHAPE@XY"]

# Open the .csv file and construct a reader using the csv module
with open(in_csv, "r") as f:
    reader = csv.reader(f)
    # Read the headers and don't try to insert this row
    headers = next(reader)
    # Construct the insertCursor for the desired input type using the fields
    # specified
    with service_area.insertCursor(
        arcpy.nax.ServiceAreaInputDataType.Facilities,
        fields
    ) as cur:
        # Iterate through the CSV file's rows and insert them as facilities
        for row in reader:
            cur.insertRow(
                [row[0], float(row[1]), (float(row[2]), float(row[3]),)]
            )

Learn more about the insertCursor method for Service Area analysis in the Methods section of the ServiceArea solver object's documentation.

Learn more about the SolverInsertCursor object

See a complete list of fields supported by each input class for a Service Area analysis

Locate inputs on the network

The input data for your network analysis rarely coincides exactly with the geometry of the streets in the network. When you perform a network analysis, the solver must determine the exact position on the network used for the analysis that should be used to represent each input location.

Learn more about network location fields and how inputs are located on the network

The arcpy.nax solver objects contain some properties that control locating behavior for all analysis inputs.

The searchSources property controls which network sources can be used for locating and optionally allows you to specify a SQL expression to restrict locating to network source features matching certain characteristics. For example, you can configure the analysis to locate inputs on streets but not on public transit lines, and you can prevent inputs from locating on street features with certain road class values.

The searchTolerance and searchToleranceUnits properties control the maximum search distance to use when locating the input features on the network. If no valid network location is found within this distance, the input feature will be considered unlocated and cannot be used in the analysis.

Learn more about these parameters for a Service Area analysis in the Properties section of the ServiceArea solver object's documentation.

Control locating behavior separately for each input class

In some circumstances, you may want to separately control locating behavior for each input class in an analysis. For example, in an OD cost matrix analysis, you could use a search query for input origins only, if that query should not apply to the input destinations and barriers.

Use the setLocateSettingsOverrides method to configure locating behavior for individual input classes, overriding the default settings for the analysis.

The following code snippet shows how to set the default search tolerance and search sources for the analysis and a search query for the input facilities only.


# Set default search tolerance and search sources for all analysis inputs
service_area.searchTolerance = 200
service_area.searchToleranceUnits = arcpy.nax.DistanceUnits.Feet
service_area.searchSources = [["Streets", ""], ["Streets_ND_Junctions", ""]]

# Set a search query for the input facilities only to prevent locating
# on highway ramps (designated, in this example, as ROAD_CLASS 3). This query
# will not apply to other inputs, such as barriers.
sources = [["Streets", "ROAD_CLASS <> 3"], ["Streets_ND_Junctions", ""]]
service_area.setLocateSettingsOverrides(
    arcpy.nax.ServiceAreaInputDataType.Facilities,
    search_sources=sources
)

Learn more about this method for a Service Area analysis in the Methods section of the ServiceArea solver object's documentation.

Use precalculated network location fields

Although inputs are added to the analysis using the load and insertCursor methods, the network locations are not calculated until the analysis is solved using the solve method. However, it is possible to use precalculated network locations by specifying network location fields in either load or insertCursor. This saves time during the solve process, and it can speed up your overall analysis if you are going to use the same input locations more than once.

Learn more about precalculating network locations

When using precalculated network location fields, you may, under some circumstances, want to set the allowAutoRelocate property to False. When this property is set to True, inputs located on portions of the network that are restricted by the selected travel mode or obstructed by barriers are relocated automatically at solve time to ensure that all locations are reachable. In many cases, this is the desired behavior; however, if you want to use your precalculated network location fields exactly as they appear in your input data, you can set the allowAutoRelocate property to False.

Use precalculated network location fields with the load method

To use precalculated network location fields when loading data, first construct field mappings using the fieldMappings method. Set the fieldMappings method's use_location_fields parameter to True. This will include the network location fields in the NAClassFieldMappings dictionary returned by the method.

As long as the precalculated network location fields in your input data have the standard names, you do not need to explicitly map your fields. They will be automatically mapped to the input class's location fields when the NAClassFieldMappings dictionary is created, and they will be used by the analysis if you use these field mappings when you call the load method. If your location fields have nonstandard names, you can map them like any other input fields.

The following code snippet shows how to load Service Area facilities with precalculated network location fields.


# Network locations have been precalculated for these inputs
# using the Calculate Locations tool. The feature class
# includes the network location fields:
# SourceID, SourceOID, PosAlong, SideOfEdge
input_facilities = "C:/data/io.gdb/FireStations"

# Construct a field mapping object with network location fields
field_mappings = service_area.fieldMappings(
    arcpy.nax.ServiceAreaInputDataType.Facilities,
    use_location_fields=True
)

# Load your input data using the field mappings
# Location fields are included automatically because
# of the field mappings.
service_area.load(
    arcpy.nax.ServiceAreaInputDataType.Facilities,
    input_facilities,
    field_mappings
)

Use precalculated network location fields with the insertCursor method

To use precalculated network location fields when inserting data with the insertCursor method, include these fields in the field_names parameter when calling the insertCursor method. You can set values for these fields just like any other field when inserting rows.

Tip:

If you insert rows using network location fields, you do not need to specify geometry for the inputs using any of the special shape tokens. The solver uses the geometry to calculate the network locations, but this is not needed if the network locations have already been determined.

The following code snippet shows how to insert Service Area facilities with precalculated network location fields using the insertCursor method.


# Define fields to use with the insertCursor
fields = ["Name", "SHAPE@XY", "SourceID", "SourceOID", "PosAlong", "SideOfEdge"]

# Define input data with network location fields
# The latitude and longitude are not strictly necessary because the inputs
# have already been located on the network.
input_data = [
    ["Fire Station 1", (-117.10191118199998, 32.634351493000054), 1, 9533, 0.5, 2],
    ["Fire Station 2", (-116.97970607599996, 32.56210221400005), 1, 7629, 0.33, 1],
    ["Fire Station 3", (-116.97141447099995, 32.654230331000065), 1, 2309, 1.0, 1],
    ["Fire Station 4", (-117.00762504, 32.70097640100005), 1, 9016, 0.62, 1]
]

# Insert the facilities
with service_area.insertCursor(arcpy.nax.ServiceAreaInputDataType.Facilities, fields) as cur:
    for input_pt in input_data:
        cur.insertRow(input_pt)

Include custom fields in analysis inputs

In some cases, you may want to include additional fields in your network analysis that do not correspond to any of the supported network analysis properties. For example, if you are modeling fire station coverage, you may want to include the number of trucks based out of each station. This information will not be used for the network analysis itself, but it will be used for reporting purposes later. It is possible to include additional fields like this in your network analysis inputs, and these fields are typically passed to the analysis outputs. For example, extra fields in the Service Area Facilities input class will be included in the ServiceAreaResult object's Facilities output class as well.

Tip:

Use the fieldNames method to verify that your custom fields have been successfully added to an analysis input class. Learn more about the fieldNames method for Service Area analysis in the Methods section of the ServiceArea solver object's documentation.

Include custom fields when using the load method

If you are loading a feature class or layer into your analysis using the load method, use the list_candidate_fields parameter fieldMappings method to include nondefault fields from your input data. This parameter accepts a list of field objects that can be obtained from the feature class or layer you plan to load. All fields included in this list will be included in the field mapping dictionary, and when you use these field mappings with the load method, all fields will be included in your analysis inputs.

The following code snippet shows how to use the fieldMappings method's list_candidate_fields parameter to include all fields from your input data in the Service Area analysis's Facilities input class.


input_facilities = "C:/data/io.gdb/FireStations"

# Construct a field mapping object
# Use the list_candidate_fields parameter to include non-default custom fields
# from the input
field_mappings = service_area.fieldMappings(
    arcpy.nax.ServiceAreaInputDataType.Facilities,
    list_candidate_fields=arcpy.ListFields(input_facilities)
)

# Load your input data using the field mappings
service_area.load(
    arcpy.nax.ServiceAreaInputDataType.Facilities,
    input_facilities,
    field_mappings
)

The following code snippet is similar to the previous one, but rather than including all fields from your input data, it shows how to include only a specific one.


input_facilities = "C:/data/io.gdb/FireStations"
extra_fields = [f for f in arcpy.ListFields(input_facilities) if f.name == "NumTrucks"]

# Construct a field mapping object
# Use the list_candidate_fields parameter to include non-default custom fields 
# from the input
field_mappings = service_area.fieldMappings(
    arcpy.nax.ServiceAreaInputDataType.Facilities,
    list_candidate_fields=extra_fields
)

# Load your input data using the field mappings
service_area.load(
    arcpy.nax.ServiceAreaInputDataType.Facilities,
    input_facilities,
    field_mappings
)

Include custom fields when using the insertCursor method

If you are inserting data directly into the analysis inputs using the insertCursor method, you must first add your desired nondefault fields to the input class using the addFields method. Once the desired fields have been added, you can include them in the field_names parameter when calling the insertCursor method and insert values as normal.

Learn more about the addFields method for Service Area analysis in the Methods section of the ServiceArea solver object's documentation.

The following code snippet shows how to add a nondefault field using the addFields method and insert rows with values for this field using the insertCursor method.


# Add a custom field called NumTrucks to the Service Area Facilities input class
field_definitions = [["NumTrucks", "SHORT"]]
service_area.addFields(arcpy.nax.ServiceAreaInputDataType.Facilities, field_definitions)

# Define fields to use with the insertCursor
fields = ["Name", "NumTrucks", "AdditionalTime", "SHAPE@XY"]
# Construct the insertCursor for the desired input type using the fields specified
with service_area.insertCursor(arcpy.nax.ServiceAreaInputDataType.Facilities, fields) as cur:
    # Insert rows. The lists of values inserted match the designated fields.
    cur.insertRow(["Fire Station 1", 1, 1, (-117.10191118199998, 32.634351493000054)])
    cur.insertRow(["Fire Station 2", 2, 1, (-116.97970607599996, 32.56210221400005)])
    cur.insertRow(["Fire Station 3", 4, 2.5, (-116.97141447099995, 32.654230331000065)])
    cur.insertRow(["Fire Station 4", 2, 1.5, (-117.00762504, 32.70097640100005)])