You can do the following using the ArcGIS Runtime SDK closest facilities task:
- Find the nearest facilities to incidents based on network travel time
- Set a maximum impedance (distance or time for example) to search for facilities
- Set the number of facilities to find for each incident
- Avoid restricted areas and maneuvers
- Specify different travel modes to model various transportation scenarios
A closest facilities task is a network analysis operation that is executed asynchronously. It returns results with details regarding the closest facilities to incidents and the routes to travel for a given transportation network. This operation is sometimes referred to as solving for closest facilities or finding closest facilities.
Transportation network datasets are modeled from features (lines and points) that represent roads, bridges, tunnels, bike paths, train tracks, junctions, and other elements in the network. The geometric intersections of features help to define connectivity between the network elements they represent. The connectivity of the transportation network is analyzed to find the best routes between incidents and facilities.
To learn more, see What is a network dataset in the ArcGIS Desktop documentation.
Choose a data source
The closest facilities task supports using online services or a local mobile geodatabase as its data source for a network.
Use an online network service
To model travel between incidents and facilities, you can use a transportation network exposed as an online ArcGIS service (an ArcGIS network analysis service). Network Analysis services are hosted in Esri's cloud platform, ArcGIS Online, or can be published on your own ArcGIS servers. These services provide a REST API for clients such as mobile and web applications. For more information about publishing a service, see Tutorial: Publishing a network analysis service.
Some geoprocessing functionality is also available locally using Local Server and geoprocessing packages.
The most important criterion for choosing a service is the area that it covers. You cannot find the closest facilities using a service that does not encompass all the incidents, facilities, and barriers that your user will specify. In addition, services offer differing levels of capability and specialization, which are determined by the service properties and the underlying transportation network dataset.
You can build a transportation network dataset from your own data, or use a dataset provided by someone else, such as Esri's World Street Map data. Then, you can publish the data as a closest facility service on your own portal or on ArcGIS Online. Or, you could use a closest facility service created by someone else that suits your needs, such as the Esri World Closest Facility Service, which references data that covers much of the world. Since the ArcGIS Online data also includes traffic for many areas, analysis can account for dynamic traffic conditions. For instance, you can find the closest facility for a departure time of 7:00 a.m. and compare that route with one you generate for 8:00 a.m.
Accessing a closest facility service may require credentials, which is also true for accessing map and feature services. You provide these credentials when you create the closest facilities task object or by adding a token to the AuthenticationManager. For more information, see Use the Authentication Manager.
When using ArcGIS Enterprise portal, default utility services may be configured, such as a closest facilities service. ArcGIS Runtime API provides methods to obtain the default services' URLs for a given portal. Setting a default service on a portal is optional, however, so they may not always be set.
Use local data
You can find closest facilities using a transportation network stored on your device, meaning that your device does not need a network connection to a communication network to find a route. You have a couple of options for provisioning your device with a local transportation network: mobile geodatabases or mobile map packages. Either may be deployed with your app on the device.
If you'd like a ready-to-use and regularly updated network dataset (and locator) for your area of interest, you can license StreetMap Premium data (in mobile map package format). For details, see Add StreetMap Premium data.
To learn more about mobile geodatabases see Creating ArcGIS Runtime content.
To learn more about mobile map packages see create a mobile map package with ArcGIS Pro.
Find the closest facilities
The basic steps for finding the closest facilities are outlined below.
- Create and load the closest facilities task
- Specify closest facility task parameters
- Execute the closest facilities task
- Process results
Create and load the closest facilities task
The closest facilities task is an object that references a closest facilities service or local transportation network and accepts parameters that define how closest facilities should be found on the network. When executed, it returns routes to the closest facilities as results.
The following example creates a closest facilities task from a public service:
var uri = new Uri("http://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiego/NAServer/ClosestFacility"); ClosestFacilityTask closestFacilityTask = await ClosestFacilityTask.CreateAsync(uri);
You can create a closest facilities task from a portal's default service (if one is defined) as follows:
// connect to the specified portal var portalUri = new Uri("http://" + portalHost + "/sharing/rest"); ArcGISPortal portal = await ArcGISPortal.CreateAsync(portalUri); // generate an authentication token for the portal TokenCredential token = await AuthenticationManager.Current.GenerateCredentialAsync(portalUri, userName, password); // get the Uri for the default closest facility service var taskUri = new Uri(portal.PortalInfo.HelperServices.ClosestFacilityService.Url); // create a closest facility task from the Uri ClosestFacilityTask task = await ClosestFacilityTask.CreateAsync(taskUri, token);
You can also create a closest facilities task from a mobile map package or mobile geodatabase stored on the device.
// open a local mobile map package (.mmpk file)
MobileMapPackage mmpk = await MobileMapPackage.OpenAsync(mapPackagePath);
// load the first map in the package
Map firstMap = mmpk.Maps;
// create a closest facility task from the first transportation network in the map
ClosestFacilityTask task = await ClosestFacilityTask.CreateAsync(firstMap.TransportationNetworks);
// create a closest facility task from a mobile geodatabase (.geodatabase) and network name
ClosestFacilityTask task = await ClosestFacilityTask.CreateAsync(geodatabasePath, "Streets_ND");
Specify closest facility task parameters
Closest facility task parameters specify how the analysis should be performed. A variety of parameters are available that let you customize how the closest facilities are determined, such as incident locations, facility locations, barriers to network travel, travel mode, target facility count, output spatial reference, and so on.
You can get the default set of closest facility task parameters from the closest facility task object. These default parameters are determined by properties of the service and the underlying transportation network dataset. Different services will most likely have different defaults. Rather than building task parameters from scratch, it's recommended to start with the default parameters for the service and to then modify individual parameter values you want to change.
The following example gets the default ClosestFacilityParameters and changes some of the parameter values:
// get the default parameters defined for the underlying service ClosestFacilityParameters parameters = await closestFacilityTask.CreateDefaultParametersAsync(); // explicitly set values for some parameters parameters.ReturnDirections = true; parameters.ReturnRoutes = true; parameters.OutputSpatialReference = myMapView.SpatialReference;
Your app might allow users to choose some parameters directly, such as cost attributes to accumulate or the number of facilities to find. Your app would get these choices from the user and set the corresponding values on the parameter object.
Specify incidents, facilities, and barriers
Incidents and facilities are points features. They can be created either explicitly from Point objects or by specifying a feature table (local or online) with a geometry of type point. When using a feature table, you can optionally set a where clause to filter which features to use. When specifying an online feature table, the table will not be queried until solve is called.
Barriers are locations along the network that either restrict travel completely (a road closure for example) or add to the cost of travel when traversed (road work or increased traffic for example).
Barriers can be points, polylines, or polygons, and you can define barriers in various ways. Your users might draw barrier graphics interactively, your app might load graphics from a feature table, and so on. For example, you may have a feature table with areas affected by a forest fire that must be avoided. Barriers are optional when solving for closest facilities but when relevant, can make your results more accurate.
The solve operation projects input features to the spatial reference of the transportation network dataset used by the service. The spatial reference of the incidents, facilities, and barriers must be defined but can be different than the transportation network dataset's spatial reference.
The following example creates incidents, facilities, and barriers objects and uses them to set properties on the closest facility parameters:
// create a incident from a location
var incidentOneLocation = new MapPoint(-13034379.71, 3858390.14, SpatialReferences.WebMercator);
var incidentOne = new Incident(incidentOneLocation);
// create a list of incidents and add to it
var incidentList = new List<Incident>();
// assign the incident list to the parameters
// create a facility from a location
var facilityOneLocation = new MapPoint(-13032763.24, 3860880.60, SpatialReferences.WebMercator);
var facilityOne = new Facility(facilityOneLocation);
// create a list of facilities and add to it
var facilityList = new List<Facility>();
// assign the facilities list to the parameters
// create a PointBarrier to avoid
var accidentLocation = new MapPoint(-13033331.71, 3859368.54, SpatialReferences.WebMercator);
var barrier = new PointBarrier(accidentLocation);
// create a list of point barriers and add to it
var pointBarriers = new List<PointBarrier>();
// assign the point barriers list to the parameters
You can also use online tables to define facilities and incidents as follows:
// open a service table of facilities
Uri facilityTableUri = new Uri("http://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiegoInputs/MapServer/0");
ServiceFeatureTable facilityServiceTable = new ServiceFeatureTable(facilityTableUri);
// create query parameters to select all facilities
QueryParameters facilityQueryParams = new QueryParameters();
facilityQueryParams.WhereClause = "1=1";
// set the task parameters facilities using the table and the query parameters
// open a service table of incidents
Uri incidentTableUri = new Uri("http://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiegoInputs/MapServer/1");
ServiceFeatureTable incidentServiceTable = new ServiceFeatureTable(incidentTableUri);
// create query parameters to select all incidents
QueryParameters incidentQueryParams = new QueryParameters();
incidentQueryParams.WhereClause = "1=1";
// set the task parameters incidents using the table and the query parameters
The following example uses a local table to define facilities:
// open a local runtime geodatabase with tables for task inputs
var geodatabasePath = @"c:\data\mydata.geodatabase";
var gdb = await Geodatabase.OpenAsync(geodatabasePath);
// get the table that contains facility features
ArcGISFeatureTable facilityTable = gdb.GeodatabaseFeatureTable("Facilities");
// create a query parameters that filters for hospitals and fire stations
var facilityQueryParams = new QueryParameters();
facilityQueryParams.WhereClause = "Type='Hospital' or Type='Fire Station'";
// set the facilities using the local table and query
// get the table that contains incident features
ArcGISFeatureTable incidentTable = gdb.GeodatabaseFeatureTable("Incidents");
// create a query parameters that gets all incidents
var incidentsQueryParams = new QueryParameters();
incidentsQueryParams.WhereClause = "1=1";
// set the incidents using the local table and query
The input table's field values can be used to populate properties for incidents, facilities, and barriers. For local geodatabases, this is based on a naming convention. For details, see Closest facility analysis. When publishing a closest facility service, the default names can be altered. For details, see Loading Network Analysis Objects in ArcMap.
If your app shows the device's location, you may wish to use the geometry of this location for the incident geometry.
Other closest facility parameters
Additional properties on the closest facility parameters object allow you to refine how travel across the network is modeled, to place thresholds on the solve, and to restrict the results that are returned. The parameters are as follows:
- Accumulate attribute names—A list of cost attributes to accumulate. Cost attributes for a particular service can be obtained from ClosestFacilityTaskInfo.CostAttributes.
- Travel mode—The type of travel used when solving for closest facilities. Travel modes on a network dataset define how a pedestrian, car, truck, or other medium of transportation moves through the network. A list of travel modes for a particular service can be returned using ClosestFacilityTaskInfo.TravelModes.
- Default target facility count—The maximum number of facilities to be found for each incident. This value must be at least 1 or more. Individual incidents can override the default value by specifying target facility count.
- Default impedance cutoff—An impedance value after which the solve will stop searching for facilities. Any facilities beyond this value will not be include in the results.
- Start time—The date and time used for solving for closest facilities. This can affect the solve result if the network data source incorporates historic or live traffic.
- Start time usage—Indicates whether the start time represents the departure or arrival along the closest facility routes.
- Travel direction—Indicates whether the direction of travel is from incidents to facilities or from facilities to incidents. On a network with one-way restrictions and traffic data, changing the travel direction can produce different results. The direction you choose depends on the nature of your analysis.
Execute the closest facilities task
To find closest facilities, call the asynchronous SolveClosestFacilityAsync method and await the result. A task parameters object (ClosestFacilityParameters) that defines input features and task preferences is passed to the method, and a ClosestFacilityResult object is returned.
// solve for closest facilities
ClosestFacilityResult solveResult = await closestFacilityTask.SolveClosestFacilityAsync(_closestFacilityParameters);
// ... process results ...
When the closest facility task completes, results are available as a ClosestFacilityResult object. Depending on the success of the task, the Messages property on the ClosestFacilityResult object may be populated. These messages contain information about how the task was solved.
Output incidents, facilities, or barriers
If the task parameters are set to return incidents, facilities, or barriers, properties of the closest facility result for these outputs are populated. These output features are copies of the corresponding inputs with additional properties such as LocationStatus and DistanceToNetworkLocation. The location of output incidents and facilities may also differ slightly from the input locations since output incidents and facilities are snapped to the closest non-restricted network element.
Incident to facility routes
Depending on the input parameters, an incident may be routed to multiple facilities. To retrieve each of these routes from the results, use the corresponding facility and incident indices. To help with this, GetRankedFacilities returns the output facilities indices in order. The following example shows how to retrieve the routes in order:
// Iterate over the solve result incidents
for (int incidentIndex = 0; incidentIndex < solveResult.Incidents.Count; incidentIndex++)
// get the rankings of the facilities for this incident (an ordered list of the returned facilities indexes)
// --Example: if the facility ranking is [2,0,1,3] then ClosestFacilityResult.Facilities is the closest, followed
// by ClosestFacilityResult.Facilities, and so on.
IReadOnlyList<int> rankedFacilitiesIndexes = solveResult.GetRankedFacilityIndexes(incidentIndex);
// using the ranked list of facilities, get the route for this incident-facility combination
// the facility indexes are in ranked order, so the 'best' route will be first, followed by the second best, and so on
foreach (var facilityIndex in rankedFacilitiesIndexes)
ClosestFacilityRoute closestFacilityRoute = solveResult.GetRoute(facilityIndex, incidentIndex);
// ... work with the closestFacilityRoute ...
Each closest facility route in the results contains the polyline between an incident and facility, as well as the time spent traveling the route and the total time (travel time plus wait time if any).
Directions are returned for each route in the results if requested in the task parameters. Depending on the service configuration, directions can be returned in more than one language. Supported languages for a service are available in the closest facility task info.
Directions are composed of direction maneuvers. Each maneuver contains properties such as the direction text, which contains instructions that describe travel through a section of a route. A direction maneuver is further broken down into maneuver messages that give details about a specific maneuver.
If defined in the transportation network, directions may also include direction events, which are notifications that do not require any action when traversing the route, for example, crossing over a time zone boundary.
Direction maneuver’s can include from level and to level values to define things like floor levels inside a building. For example, a user may want to see just one level of a walking route that spans multiple floors.
In order to return from level and to level values for a route:
- The route’s directions style must be Campus
- The network dataset must be configured to support levels as described in the Setting directions topic in the desktop help. This applies to both local data and the underlying network used by a routing service
- The ArcGIS Server version hosting the service must be 10.6 or higher (when using online routing services)