Créer une couche d’analyse de tournée de véhicules (Network Analyst)

Synthèse

Crée une couche d’analyse de réseau de tournée de véhicules (VRP) et définit ses propriétés d’analyse. Une couche d’analyse de réseau de tournée de véhicules s’avère utile pour optimiser un ensemble de tournées au moyen d’une flotte de véhicules. La couche peut être créée à l'aide d'un jeu de données réseau local ou d'un service hébergé en ligne ou sur un portail.

Utilisation

  • Après avoir créé la couche d’analyse avec cet outil, vous pouvez ajouter des objets d’analyse de réseau à l’aide de l’outil Ajouter des localisations, résoudre l’analyse à l’aide de l’outil Calculer et enregistrer les résultats sur le disque à l’aide de l’outil Enregistrer dans un fichier de couche.

  • Lorsque vous utilisez cet outil dans des modèles de géotraitement, si le modèle est exécuté en tant qu’outil, la couche d’analyse de réseau en sortie doit être convertie en paramètre de modèle. Dans le cas contraire, la couche en sortie n’est pas ajoutée au contenu de la carte.

Paramètres

ÉtiquetteExplicationType de données
Source de données réseau

Jeu de données réseau ou service sur lequel l'analyse du réseau est effectuée. Utilisez l'URL du portail pour un service.

Network Dataset Layer; String
Nom de la couche
(Facultatif)

Nom de la couche d’analyse de réseau VRP à créer.

String
Mode de déplacement
(Facultatif)

Nom du mode de déplacement à utiliser dans l’analyse. Le mode de déplacement représente une collection de paramètres réseau, tels que des restrictions de circulation et des règles de demi-tour, qui détermine la façon dont un piéton, une voiture, un camion ou un autre mode de transport se déplace sur le réseau. Les modes de déplacement sont définis dans votre source de données de réseau. Vous pouvez aussi utiliser un objet arcpy.na.TravelMode et une chaîne contenant la représentation JSON valide d’un mode de déplacement en entrée du paramètre.

VRP ne s’opérant qu’avec une impédance temporelle, seuls les modes de déplacement d’impédance temporelle peuvent être sélectionnés.

String
Unités du champ temporel
(Facultatif)

Indique les unités de temps à utiliser par les champs temporels des sous-couches et des tables de la couche d’analyse (classes d’analyse de réseau). Cette valeur n’a pas besoin d’être identique aux unités de l’attribut de coût temporel.

Pour en savoir plus sur les attributs de coût

  • MinutesLes unités de temps sont les minutes. Il s’agit de l’option par défaut.
  • SecondesLes unités de temps sont les secondes.
  • HeuresLes unités de temps sont les heures.
  • JoursLes unités de temps sont les jours.
String
Unités du champ de distance
(Facultatif)

Indique les unités de distance à utiliser par les champs de distance des sous-couches et des tables de la couche d’analyse (classes d’analyse de réseau). Cette valeur n’a pas besoin d’être identique aux unités de l’attribut de coût de distance facultatif.

Pour en savoir plus sur les attributs de coût

  • MilesLes milles sont les unités de distance. Il s’agit de l’option par défaut.
  • KilomètresLes unités de distance sont les kilomètres.
  • PiedsLes unités de distance sont les pieds.
  • YardsLes unités de distance sont les yards.
  • MètresLes unités de distance sont les mètres.
  • PoucesLes unités de distance sont les pouces.
  • CentimètresLes unités de distance sont les centimètres.
  • MillimètresLes unités de distance sont les millimètres.
  • DécimètresLes unités de distance sont les décimètres.
  • Milles nautiquesLes unités de distance sont les milles nautiques.
String
Date par défaut
(Facultatif)

Date implicite pour les valeurs de champs temporels n'ayant pas de date spécifiée avec l'heure. Si un champ temporel pour un objet d’ordre, tel que TimeWindowStart, a une valeur d’heure uniquement, la date est supposée être la date par défaut. La date par défaut n'a aucune incidence sur les valeurs de champs horaires déjà dotées d'une date.

Configurez votre analyse pour utiliser une des dates spéciales suivantes pour modéliser un jour de la semaine ou la date actuelle au lieu d’une date statique spécifique :

  • Aujourd’hui - 12/30/1899
  • Dimanche - 12/31/1899
  • Lundi - 1/1/1900
  • Mardi - 1/2/1900
  • Mercredi - 1/3/1900
  • Jeudi - 1/4/1900
  • Vendredi - 1/5/1900
  • Samedi - 1/6/1900

En savoir plus sur l’utilisation des dates et des heures et sur leur interprétation dans une analyse de réseau

Date
Fuseau horaire des champs temporels
(Facultatif)

Spécifie le fuseau horaire à utiliser pour les champs date-heure en entrée pris en charge par l’outil.

Le fait de spécifier les valeurs date-heure en UTC est utile si vous ne connaissez pas le fuseau horaire dans lequel les ordres ou les dépôts se situent ou si vous avez des ordres ou des dépôts dans plusieurs fuseaux horaires et si vous souhaitez que toutes les valeurs date-heures commencent en simultané. L’option UTC s’applique uniquement lorsque votre jeu de données réseau définit un attribut de fuseau horaire. Sinon, toutes les valeurs date-heure sont traitées comme le fuseau horaire correspondant à cette localisation.

  • Heure locale des emplacementsLes valeurs date-heure associées aux ordres ou dépôts figurent dans le fuseau horaire dans lequel les ordres et les dépôts se trouvent. Pour les itinéraires, les valeurs date-heure sont basées sur le fuseau horaire où se trouve le dépôt de début pour l’itinéraire. Si un itinéraire n’a pas de dépôt de départ, tous les ordres et dépôts de l’ensemble des itinéraires doivent être dans un seul et même fuseau horaire. Pour les interruptions, les valeurs date-heure sont basées sur le fuseau horaire des itinéraires. Il s’agit de l’option par défaut.
  • UTCLes valeurs date-heure associées aux ordres ou dépôts sont exprimées selon le temps universel coordonné (UTC) et ne sont pas basées sur le fuseau horaire dans lequel les ordres ou les dépôts se trouvent.
String
Forme de l’itinéraire en sortie
(Facultatif)

Indique le type de forme qui sera utilisé pour les entités itinéraires générées par l’analyse.

Quel que soit le type de forme en sortie spécifié, le meilleur itinéraire est toujours déterminé par l’impédance du réseau, jamais par la distance euclidienne. Cela signifie que seules les formes d'itinéraire sont différentes, pas le parcours du réseau sous-jacent.

  • Le long du réseauLes itinéraires en sortie ont la forme exacte des sources de données du réseau sous-jacentes. La sortie comprend des mesures d’itinéraire pour le référencement linéaire. Les mesures augmentent à partir du premier arrêt et enregistrent l'impédance cumulée pour atteindre une position donnée.
  • Aucune ligneAucune forme n’est créée pour les itinéraires en sortie.
  • Lignes droitesLa forme d'itinéraire en sortie est une ligne droite unique entre les arrêts.Cette option n'est pas disponible si la source de données réseau sélectionnée correspond à un service.
String
Importance de la violation des fenêtres horaires
(Facultatif)

Indique l’importance de respecter les fenêtres horaires sans provoquer d’infractions. Une infraction de fenêtre horaire se produit quand un itinéraire arrive à un ordre, un dépôt ou une borne après la fermeture d'une fenêtre horaire. La violation est le laps de temps écoulé entre la fin de la fenêtre horaire et l'heure d'arrivée d'une tournée.

  • ElevéeLe solveur recherche une solution qui réduit les infractions de fenêtre horaire mais augmente le temps de trajet total. Sélectionnez ce paramètre s’il est plus important d’arriver à l’heure aux ordres que de réduire le coût global de la solution. Ce peut être le cas si vous rencontrez des clients à vos ordres et que vous ne voulez pas les importuner par des arrivées tardives (une autre option consiste à utiliser des fenêtres horaire strictes qui ne peuvent pas être enfreintes).En raison d'autres contraintes d'une tournée de véhicules, il peut s'avérer impossible de visiter tous les ordres dans leur fenêtre horaire. Dans ce cas, même l’option Élevée peut entraîner des violations.
  • MoyenneLe solveur recherche un équilibre entre le respect des fenêtres horaires et la réduction du coût total de la solution. Il s’agit de l’option par défaut.
  • FaibleLe solveur recherche une solution qui réduit le temps de trajet total, quelles que soient les fenêtres horaires. Sélectionnez ce paramètre si le respect des fenêtres horaires est moins important que la réduction du coût global de la solution. Vous pouvez utiliser ce paramètre si vous avez un retard croissant de traitement des demandes de service. Si vous souhaitez desservir un plus grand nombre d’ordres au cours d’une journée ou rattraper un retard, vous pouvez sélectionner ce paramètre, même si les clients risquent d’être gênés par l’arrivée tardive de votre flotte.
String
Importance du temps de transit excessif
(Facultatif)

Indique l’importance de réduire le temps de transit excessif. Le temps de transit excessif correspond au temps dépassant le temps nécessaire pour effectuer le trajet direct entre des couples d’ordres. Le temps excessif découle de pauses ou de trajets vers d’autres ordres ou dépôts entres des visites à des ordres appariés. Ce paramètre n’est pertinent que si vous utilisez des couples d’ordres.

Pour en savoir plus sur les couples d’ordres

  • ElevéeLe solveur recherche une solution présentant moins de temps de transit excessif entre des ordres appariés, mais augmente les coûts de trajet globaux. Utilisez ce paramètre si vous transportez des personnes entre des couples d’ordres et que voulez raccourcir le temps de trajet. Il est caractéristique des services de taxi.
  • MoyenneLe solveur recherche un équilibre entre la réduction du temps de transit excessif et la réduction du coût total de la solution. Il s’agit de l’option par défaut.
  • FaibleLe solveur recherche une solution qui réduit le coût total de la solution, quel que soit le temps de transit excessif. Ce paramètre est fréquemment utilisé par les messageries privées. Comme les sociétés de messagerie transportent des colis et non des personnes, le temps de trajet n’est pas aussi important. Ce paramètre permet aux messageries de servir les couples d’ordres dans le bon ordre et de réduire le coût global de la solution.
String
Générer des feuilles de route lors du calcul
(Facultatif)

Indique si des feuilles de route sont générées.

  • Activé : une feuille de route détaillée sera générée lors de la résolution. Il s’agit de l’option par défaut.
  • Désactivé : aucune feuille de route détaillée ne sera générée lors de la résolution.
Boolean
Agrégation spatiale
(Facultatif)

Indique si l’agrégation spatiale sera utilisée.

  • Coché : les ordres affectés à un itinéraire individuel sont agrégés spatialement. L’agrégation des ordres permet de regrouper les itinéraires dans des zones plus petites et de réduire le nombre d’intersections entre lignes d’itinéraires, mais elle peut augmenter le temps de trajet total. Il s’agit de l’option par défaut.
  • Non coché : le solveur ne hiérarchise pas les ordres d’agrégation spatiale et les lignes d’itinéraires peuvent s’intersecter. Utilisez cette option si des zones d’itinéraire sont spécifiées.
Boolean
Ignorer les localisations non valides lors de l’analyse
(Facultatif)

Détermine si les emplacements en entrée non valides sont ignorés.

  • Activé : les localisations en entrée non valides seront ignorées afin que l’analyse aboutisse avec uniquement les localisations valides.
  • Désactivé : les localisations non valides ne seront pas ignorées et provoqueront l’échec de l’analyse. Il s’agit de l’option par défaut.
Boolean

Sortie obtenue

ÉtiquetteExplicationType de données
Couche Network Analyst

Nouvelle couche d’analyse de réseau.

Network Analyst Layer

arcpy.management.MakeVehicleRoutingProblemAnalysisLayer(network_data_source, {layer_name}, {travel_mode}, {time_units}, {distance_units}, {default_date}, {time_zone_for_time_fields}, {line_shape}, {time_window_factor}, {excess_transit_factor}, {generate_directions_on_solve}, {spatial_clustering}, {ignore_invalid_locations})
NomExplicationType de données
network_data_source

Jeu de données réseau ou service sur lequel l'analyse du réseau est effectuée. Utilisez l'URL du portail pour un service.

Network Dataset Layer; String
layer_name
(Facultatif)

Nom de la couche d’analyse de réseau VRP à créer.

String
travel_mode
(Facultatif)

Nom du mode de déplacement à utiliser dans l’analyse. Le mode de déplacement représente une collection de paramètres réseau, tels que des restrictions de circulation et des règles de demi-tour, qui détermine la façon dont un piéton, une voiture, un camion ou un autre mode de transport se déplace sur le réseau. Les modes de déplacement sont définis dans votre source de données de réseau. Vous pouvez aussi utiliser un objet arcpy.na.TravelMode et une chaîne contenant la représentation JSON valide d’un mode de déplacement en entrée du paramètre.

String
time_units
(Facultatif)

Indique les unités de temps à utiliser par les champs temporels des sous-couches et des tables de la couche d’analyse (classes d’analyse de réseau). Cette valeur n’a pas besoin d’être identique aux unités de l’attribut de coût temporel.

Pour en savoir plus sur les attributs de coût

  • MinutesLes unités de temps sont les minutes. Il s’agit de l’option par défaut.
  • SecondsLes unités de temps sont les secondes.
  • HoursLes unités de temps sont les heures.
  • DaysLes unités de temps sont les jours.
String
distance_units
(Facultatif)

Indique les unités de distance à utiliser par les champs de distance des sous-couches et des tables de la couche d’analyse (classes d’analyse de réseau). Cette valeur n’a pas besoin d’être identique aux unités de l’attribut de coût de distance facultatif.

Pour en savoir plus sur les attributs de coût

  • MilesLes milles sont les unités de distance. Il s’agit de l’option par défaut.
  • KilometersLes unités de distance sont les kilomètres.
  • FeetLes unités de distance sont les pieds.
  • YardsLes unités de distance sont les yards.
  • MetersLes unités de distance sont les mètres.
  • InchesLes unités de distance sont les pouces.
  • CentimetersLes unités de distance sont les centimètres.
  • MillimetersLes unités de distance sont les millimètres.
  • DecimetersLes unités de distance sont les décimètres.
  • NauticalMilesLes unités de distance sont les milles nautiques.
String
default_date
(Facultatif)

Date implicite pour les valeurs de champs temporels n'ayant pas de date spécifiée avec l'heure. Si un champ temporel pour un objet d’ordre, tel que TimeWindowStart, a une valeur d’heure uniquement, la date est supposée être la date par défaut. La date par défaut n'a aucune incidence sur les valeurs de champs horaires déjà dotées d'une date.

Configurez votre analyse pour utiliser une des dates spéciales suivantes pour modéliser un jour de la semaine ou la date actuelle au lieu d’une date statique spécifique :

  • Aujourd’hui - 12/30/1899
  • Dimanche - 12/31/1899
  • Lundi - 1/1/1900
  • Mardi - 1/2/1900
  • Mercredi - 1/3/1900
  • Jeudi - 1/4/1900
  • Vendredi - 1/5/1900
  • Samedi - 1/6/1900

En savoir plus sur l’utilisation des dates et des heures et sur leur interprétation dans une analyse de réseau

Date
time_zone_for_time_fields
(Facultatif)

Spécifie le fuseau horaire à utiliser pour les champs date-heure en entrée pris en charge par l’outil.

  • LOCAL_TIME_AT_LOCATIONSLes valeurs date-heure associées aux ordres ou dépôts figurent dans le fuseau horaire dans lequel les ordres et les dépôts se trouvent. Pour les itinéraires, les valeurs date-heure sont basées sur le fuseau horaire où se trouve le dépôt de début pour l’itinéraire. Si un itinéraire n’a pas de dépôt de départ, tous les ordres et dépôts de l’ensemble des itinéraires doivent être dans un seul et même fuseau horaire. Pour les interruptions, les valeurs date-heure sont basées sur le fuseau horaire des itinéraires. Il s’agit de l’option par défaut.
  • UTCLes valeurs date-heure associées aux ordres ou dépôts sont exprimées selon le temps universel coordonné (UTC) et ne sont pas basées sur le fuseau horaire dans lequel les ordres ou les dépôts se trouvent.

Le fait de spécifier les valeurs date-heure en UTC est utile si vous ne connaissez pas le fuseau horaire dans lequel les ordres ou les dépôts se situent ou si vous avez des ordres ou des dépôts dans plusieurs fuseaux horaires et si vous souhaitez que toutes les valeurs date-heures commencent en simultané. L’option UTC s’applique uniquement lorsque votre jeu de données réseau définit un attribut de fuseau horaire. Sinon, toutes les valeurs date-heure sont traitées comme le fuseau horaire correspondant à cette localisation.

String
line_shape
(Facultatif)

Indique le type de forme qui sera utilisé pour les entités itinéraires générées par l’analyse.

  • ALONG_NETWORKLes itinéraires en sortie ont la forme exacte des sources de données du réseau sous-jacentes. La sortie comprend des mesures d’itinéraire pour le référencement linéaire. Les mesures augmentent à partir du premier arrêt et enregistrent l'impédance cumulée pour atteindre une position donnée.
  • NO_LINESAucune forme n’est créée pour les itinéraires en sortie.
  • STRAIGHT_LINESLa forme d'itinéraire en sortie est une ligne droite unique entre les arrêts.Cette option n'est pas disponible si la source de données réseau sélectionnée correspond à un service.

Quel que soit le type de forme en sortie spécifié, le meilleur itinéraire est toujours déterminé par l’impédance du réseau, jamais par la distance euclidienne. Cela signifie que seules les formes d'itinéraire sont différentes, pas le parcours du réseau sous-jacent.

String
time_window_factor
(Facultatif)

Indique l’importance de respecter les fenêtres horaires sans provoquer d’infractions. Une infraction de fenêtre horaire se produit quand un itinéraire arrive à un ordre, un dépôt ou une borne après la fermeture d'une fenêtre horaire. La violation est le laps de temps écoulé entre la fin de la fenêtre horaire et l'heure d'arrivée d'une tournée.

  • HighLe solveur recherche une solution qui réduit les infractions de fenêtre horaire mais augmente le temps de trajet total. Sélectionnez ce paramètre s’il est plus important d’arriver à l’heure aux ordres que de réduire le coût global de la solution. Ce peut être le cas si vous rencontrez des clients à vos ordres et que vous ne voulez pas les importuner par des arrivées tardives (une autre option consiste à utiliser des fenêtres horaire strictes qui ne peuvent pas être enfreintes).En raison d'autres contraintes d'une tournée de véhicules, il peut s'avérer impossible de visiter tous les ordres dans leur fenêtre horaire. Dans ce cas, même l’option Élevée peut entraîner des violations.
  • MediumLe solveur recherche un équilibre entre le respect des fenêtres horaires et la réduction du coût total de la solution. Il s’agit de l’option par défaut.
  • LowLe solveur recherche une solution qui réduit le temps de trajet total, quelles que soient les fenêtres horaires. Sélectionnez ce paramètre si le respect des fenêtres horaires est moins important que la réduction du coût global de la solution. Vous pouvez utiliser ce paramètre si vous avez un retard croissant de traitement des demandes de service. Si vous souhaitez desservir un plus grand nombre d’ordres au cours d’une journée ou rattraper un retard, vous pouvez sélectionner ce paramètre, même si les clients risquent d’être gênés par l’arrivée tardive de votre flotte.
String
excess_transit_factor
(Facultatif)

Indique l’importance de réduire le temps de transit excessif. Le temps de transit excessif correspond au temps dépassant le temps nécessaire pour effectuer le trajet direct entre des couples d’ordres. Le temps excessif découle de pauses ou de trajets vers d’autres ordres ou dépôts entres des visites à des ordres appariés. Ce paramètre n’est pertinent que si vous utilisez des couples d’ordres.

Pour en savoir plus sur les couples d’ordres

  • HighLe solveur recherche une solution présentant moins de temps de transit excessif entre des ordres appariés, mais augmente les coûts de trajet globaux. Utilisez ce paramètre si vous transportez des personnes entre des couples d’ordres et que voulez raccourcir le temps de trajet. Il est caractéristique des services de taxi.
  • MediumLe solveur recherche un équilibre entre la réduction du temps de transit excessif et la réduction du coût total de la solution. Il s’agit de l’option par défaut.
  • LowLe solveur recherche une solution qui réduit le coût total de la solution, quel que soit le temps de transit excessif. Ce paramètre est fréquemment utilisé par les messageries privées. Comme les sociétés de messagerie transportent des colis et non des personnes, le temps de trajet n’est pas aussi important. Ce paramètre permet aux messageries de servir les couples d’ordres dans le bon ordre et de réduire le coût global de la solution.
String
generate_directions_on_solve
(Facultatif)

Indique si des feuilles de route sont générées.

  • DIRECTIONSUne feuille de route détaillée sera générée lors de la résolution. Il s’agit de l’option par défaut.
  • NO_DIRECTIONSAucune feuille de route détaillée ne sera générée lors de la résolution.
Boolean
spatial_clustering
(Facultatif)

Indique si l’agrégation spatiale sera utilisée.

  • CLUSTERLes ordres affectés à un itinéraire individuel sont agrégés spatialement. L’agrégation des ordres permet de regrouper les itinéraires dans des zones plus petites et de réduire le nombre d’intersections entre lignes d’itinéraires, mais elle peut augmenter le temps de trajet total. Il s’agit de l’option par défaut.
  • NO_CLUSTERLe solveur ne hiérarchise pas les ordres d’agrégation spatiale et les lignes d’itinéraires peuvent s’intersecter. Utilisez cette option si des zones d’itinéraire sont spécifiées.
Boolean
ignore_invalid_locations
(Facultatif)

Détermine si les emplacements en entrée non valides sont ignorés.

  • SKIPLes localisations en entrée non valides seront ignorées afin que l’analyse aboutisse avec uniquement les localisations valides.
  • HALTLess localisations non valides ne seront pas ignorées et provoqueront l’échec de l’analyse. Il s’agit de l’option par défaut.
Boolean

Sortie obtenue

NomExplicationType de données
out_network_analysis_layer

Nouvelle couche d’analyse de réseau.

Network Analyst Layer

Exemple de code

Exemple 1 d’utilisation de l’outil MakeVehicleRoutingProblemAnalysisLayer (fenêtre Python)

Exécute l’outil uniquement avec les paramètres requis.

import arcpy 
arcpy.env.workspace = "C:/Data/SanFrancisco.gdb" 
arcpy.na.MakeVehicleRoutingProblemAnalysisLayer("Transportation/Streets_ND")
Exemple 2 d’utilisation de l’outil MakeVehicleRoutingProblemAnalysisLayer (fenêtre Python)

Exécutez l’outil avec tous les paramètres.

import arcpy
arcpy.env.workspace = "C:/Data/SanFrancisco.gdb"
arcpy.na.MakeVehicleRoutingProblemAnalysisLayer('Streets_ND', 'FridayRoutes', 
                                                'Driving Time', 'Minutes', 
                                                'Miles', '1/2/2020', 
                                                'LOCAL_TIME_AT_LOCATIONS', 
                                                'TRUE_LINES_WITHOUT_MEASURES', 
                                                'High', 'Medium', 'DIRECTIONS')
Exemple 3 d’utilisation de l’outil MakeVehicleRoutingProblemAnalysisLayer (processus)

Le script Python autonome suivant montre comment l’utilisation de la fonction MakeVehicleRoutingProblemAnalysisLayer permet de desservir un ensemble d’ordres avec une flotte de véhicules.

# Name: MakeVRPAnalysisLayer_Ex3_Workflow.py
# Description: Find the best routes for a fleet of vehicles, which is operated
#              by a distribution company, to deliver goods from a main
#              distribution center to a set of grocery stores.
# Requirements: Network Analyst Extension

# Import system modules
import arcpy
import os

try:
    # Check out the Network Analyst license if available.
    # Fail if the Network Analyst
    # license is not available.
    if arcpy.CheckExtension("network") == "Available":
        arcpy.CheckOutExtension("network")
    else:
        raise arcpy.ExecuteError("Network Analyst Extension license is not available.")

    # Set environment settings
    output_dir = r"C:\Data"
    # The NA layer's data will be saved to the workspace specified here
    arcpy.env.workspace = os.path.join(output_dir, "Output.gdb")
    arcpy.env.overwriteOutput = True

    # Set local variables
    input_gdb = "C:/Data/SanFrancisco.gdb"
    network = os.path.join(input_gdb, "Transportation", "Streets_ND")
    layer_name = "StoreDeliveryRoute"
    travel_mode = "Driving Time"
    time_units = "Minutes"
    distance_units = "Miles"
    in_orders = os.path.join(input_gdb, "Analysis", "Stores")
    in_depots = os.path.join(input_gdb, "Analysis", "DistributionCenter")
    in_routes = os.path.join(input_gdb, "Analysis", "Routes")
    output_layer_file = os.path.join(output_dir, layer_name + ".lyrx")

    # Create a new Vehicle Routing Problem (VRP) layer. Since the time-based
    # attributes such as ServiceTime on orders and CostPerUnitTime on routes is
    # recorded in minutes, we use minutes for time_units parameter. As we are
    # using cost per unit distance in routes, we have to specify a distance
    # attribute. The values for CosterPerUnitDistance are in miles, so we
    # specify miles for distance units parameter
    result_object = arcpy.na.MakeVehicleRoutingProblemAnalysisLayer(
        network, layer_name, travel_mode, time_units, distance_units,
        line_shape="STRAIGHT_LINES")

    # Get the layer object form the result object. The route layer can now be
    # referenced using the layer object.
    layer_object = result_object.getOutput(0)

    # Get the names of all the sublayers within the VRP layer.
    sub_layer_names = arcpy.na.GetNAClassNames(layer_object)
    # Store the layer names that we will use later
    orders_layer_name = sub_layer_names["Orders"]
    depots_layer_name = sub_layer_names["Depots"]
    routes_layer_name = sub_layer_names["Routes"]

    # Load the store locations as orders. Using field mappings we map the
    # TimeWindowStart1, TimeWindowEnd1, and DeliveryQuantities properties
    # for Orders from the fields of store features and assign a value of
    # 0 to MaxViolationTime1 property. The Name and ServiceTime properties
    # have the correct mapped field names when using the candidate fields
    # from store locations feature class.
    candidate_fields = arcpy.ListFields(in_orders)
    order_field_mappings = arcpy.na.NAClassFieldMappings(layer_object, orders_layer_name, False, candidate_fields)
    order_field_mappings["TimeWindowStart"].mappedFieldName = "TimeStart1"
    order_field_mappings["TimeWindowEnd"].mappedFieldName = "TimeEnd1"
    order_field_mappings["DeliveryQuantity_1"].mappedFieldName = "Demand"
    order_field_mappings["MaxViolationTime"].defaultValue = 0
    arcpy.na.AddLocations(layer_object, orders_layer_name, in_orders, order_field_mappings, "")

    # Load the depots from the distribution center features. Using field mappings
    # we map the Name properties for Depots from the fields of distribution
    # center features and assign a value of 8 AM for TimeWindowStart1 and a
    # value of 5 PM for TimeWindowEnd1 properties
    depot_field_mappings = arcpy.na.NAClassFieldMappings(layer_object, depots_layer_name)
    depot_field_mappings["Name"].mappedFieldName = "Name"
    depot_field_mappings["TimeWindowStart"].defaultValue = "8 AM"
    depot_field_mappings["TimeWindowEnd"].defaultValue = "5 PM"
    arcpy.na.AddLocations(layer_object, depots_layer_name, in_depots, depot_field_mappings, "")

    # Load the routes from a table containing information about routes. In this
    # case, since the fields on the routes table and property names for Routes
    # are the same, we will just use the default field mappings
    routes_field_mappings = arcpy.na.NAClassFieldMappings(layer_object, routes_layer_name)
    routes_field_mappings["Name"].mappedFieldName = "Name"
    routes_field_mappings["StartDepotName"].mappedFieldName = "StartDepotName"
    routes_field_mappings["EndDepotName"].mappedFieldName = "EndDepotName"
    routes_field_mappings["StartDepotServiceTime"].mappedFieldName = "StartDepotServiceTime"
    routes_field_mappings["Capacity_1"].mappedFieldName = "Capacities"
    routes_field_mappings["CostPerUnitTime"].mappedFieldName = "CostPerUnitTime"
    routes_field_mappings["CostPerUnitDistance"].mappedFieldName = "CostPerUnitDistance"
    routes_field_mappings["MaxOrderCount"].mappedFieldName = "MaxOrderCount"
    routes_field_mappings["MaxTotalTime"].mappedFieldName = "MaxTotalTime"
    routes_field_mappings["MaxTotalTravelTime"].mappedFieldName = "MaxTotalTravelTime"
    routes_field_mappings["MaxTotalDistance"].mappedFieldName = "MaxTotalDistance"
    arcpy.na.AddLocations(layer_object, routes_layer_name, in_routes, routes_field_mappings, "")

    # Solve the VRP layer
    arcpy.na.Solve(layer_object)

    # Save the solved VRP layer as a layer file on disk with relative paths
    arcpy.management.SaveToLayerFile(layer_object, output_layer_file, "RELATIVE")

    print("Script Completed Successfully")

except Exception as e:
    # If an error occurred, print line number and error message
    import traceback
    import sys
    tb = sys.exc_info()[2]
    print("An error occurred on line %i" % tb.tb_lineno)
    print(str(e))