Closest Facility Task

A Closest Facility task allows you to find facilities that are closest to a given incident using ArcGIS Network Analyst services.

A Network Analyst service contains one or more Network Analysis layers. ArcGIS provides different types of analysis layers, such as Route, Service Area, Closest Facility, etc depending upon the type of analysis to be performed. A Closest Facility task relies on a Closest Facility analysis layer.

Learn about creating Network Analyst services

Fnding the closest hospital to an accident, the closest police cars to a crime scene, and the closest store to a customer's address are all examples of closest facility problems. When finding closest facilities, you can specify how many to find and whether the direction of travel is toward or away from them. Once you've found the closest facilities, you can display the best route to or from them, return the travel cost for each route, and display directions to each facility. Additionally, you can specify an impedance cutoff beyond which a facility should not be searched. For instance, you can set up a closest facility problem to search for hospitals within 15 minutes' drive time of the site of an accident. Any hospitals that take longer than 15 minutes to reach will not be included in the results.

Creating a Closest Facility task

To instantiate a Closest Facility task, you need to provide a URL to REST resource that represents a Closest Facility layer in a Network Analyst service. If the service is secured, you will also need to provide the credentials that can be used to access the service. The following code snippet shows how to create a Sevice Area task for the Closest Facility layer in the USA service on ArcGIS Online sample server.

NSURL* url = [NSURL URLWithString: @"http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/NAServer/Closest%20Facility";
AGSClosestFacilityTask* cfTask = [[AGSClosestFacilityTask alloc] initWithURL: url];

When you create the task, you need to ensure you take ownership of it. Otherwise, its memory might get deallocated before it has a chance to execute. If you instantiate the task using alloc as shown above , you automatically have ownership. However, if you instantiate the task using a convenience constructor, for instance closestFacilityTaskWithURL: , you explicitly need to take ownership by either sending it a retain message, or by assigning it to a property that will retain it.

When you are finished using the task, you should relinquish ownership so that its memory can be reclaimed. You do this either by sending the task a release message or by setting the corresponding property to nil.

NoteNote:

Please refer to Apple's Memory Management Programming Guide for more information on how to manage object memory.

Preparing the input

You provide input to the Closest Facility task using an object of AGSClosestFacilityTaskParameters class. You can instantiate a new AGSClosestFacilityTaskParameters object and modify its properties to provide the input. But many times, you may want to use the defaults specified in the service. In such cases, you first need to invoke retrieveDefaultClosestFacilityTaskParameters on AGSClosestFacilityTask to get the default values. The default values are returned to the task's delegate as an AGSClosestFacilityTaskParameters object via the closestFacilityTask:operation:didRetrieveDefaultClosestFacilityTaskParameters: method.

The following sections describe some of the inputs you can provide to a Closest Facility task.

Impedance

Impedance specifies a cost that should be minimized when finding the closest facility. For example, impedance could be set to Time in order to find a facility that is quickest to reach. Or it could be set to Length to find a facility that is nearest to the incident. Impedances supported by the service are listed in the Services Directory under Network Dataset -> Network Attributes with a Usage Type of esriNAUTCost

Impedances

In the example shown above, Length and Time are supported impedances. The following code snippets sets the impedance to Time .

AGSClosestFacilityTaskParameters* params = ...;
params.impedanceAttributeName = @"Time";

Cutoff

You can also specify a cutoff value for the impedance beyond which facilities should be not found. For instance, while locating the closest hospitals from the site of an accident, a cutoff value of 15 minutes would mean that the service should search for the closest hospital within 15 minutes from the incident. If the closest hospital is 17 minutes away, no hospitals will be returned in the closest facility search. A cutoff value is especially useful when searching for multiple facilities.

Facility count

Sometimes you may want to find multiple closest facilities from an incident, for instance, the nearest three fire stations to a fire. You can do this by setting the defaultTargetFacilityCount property.

params.defaultTargetFacilityCount = 3;

Facilities

Facilities represent locations that can serve as the starting point or ending point of a closest facility analysis. For eg, a fire station, or a hospital.

There are two ways to specify facilities:

Features

You can create an array of AGSFacilityGraphic objects representing facility features. You can then assign these features to the parameter object using setFacilitiesWithFeatures:. You need to minimally provide a name and a point geometry for each facility, but you can also provide additional attributes depending upon how the Closest Facility layer in the Network Analyst service is configured. These attributes are listed in the Network Analysis Classes -> Class Name:Facilities section of the Services Directory.

Attributes of facilities

Attributes may be of type input, output, or both. Input attributes are specified by the client and are taken into consideration by the service while performing the analysis.

For example, you can set an impedance on each facility if you want to assign an additional cost to that facility. The attribute should have a name Attr_<Impedance> where <impedance> is the value specified in the impedance property. For example, an attribute of name Attr_Time with a value of 5 specifies that an additional cost of 5 minutes be associated with the facility. This may be needed, for example, to account for how much time it takes the crew of a fire station to don the appropriate protective equipment and exit the fire station.

Output attributes are returned by the service along with the computed result when you enable returnFacilities on AGSClosestFacilityTaskParameters. Output attributes provide additional information about facilities pertaining to the results.

Some attributes can be both Input and Output. These attributes are specified by the client but can be modified or overriden by the service.

Learn more about attributes supported by facility features.

The symbol and infoTemplateDelegate properties on AGSFacilityGraphic are relevant only if you add the facilities to a graphics layer in order to display them on a map. They are not used in the analysis.

Layer Definition

Apart from specifying facilities by-value (i.e providing the actual values for each feature as described above), you can also specify them by-reference. This is useful when you already have a set of well known or commonly used facilities stored along with the Network Analyst service. In such cases, the application does not need to know the actual details about each facility. All it needs to do is set up a layer definition specifiying which facilities should be included in the analysis.

A layer definition is represented by an object of the AGSNALayerDefinition class. You can use SQL statements and/or Spatial relationships to specify which faciliteis should be used in the analysis. For example, the following code snippets sets up a layer definition referencing features that fall within the City of Los Angeles and have a value of "Hospital" for the facility_type attribute.

AGSNALayerDefinition* layerDef = [[[AGSNALayerDefinition alloc] init] autorelease];
layerDef.layerName = @"<layer_in_the_service_containing_facilities>";
layerDef.where = @"facility_type = 'Hospital'";
layerDef.geometry = losangelesPolygon;
layerDef.spatialRelationship = AGSSpatialRelationshipContains ;

Once you have set up a layer definition identifying the features you wish to use, you can use it to specify facilities using setFacilitiesWithLayerDefinition:method on an AGSClosestFacilityTaskParameters object.

[params setFacilitiesWithLayerDefinition: layerDef];

Incidents

Incidents also represent locations that can serve as the starting point or ending point of a closest facility analysis. For eg, a fire, or a traffic accident.

Just as with facilities, there are two ways to specify incidents:

Features

You can create an array of AGSIncidentGraphic objects representing incident features.You need to minimally provide a name and a point geometry for each incident, but you can also provide additional attributes depending upon how the Closest Facility layer in the Network Analyst service is configured. These attributes are listed in the Network Analysis Classes -> Class Name:Incidents section of the Services Directory. Attributes may be of type input (specified by the client), output (returned by the server), or both.

Learn more about the attributes supported by Incident features

Layer Definition

Apart from specifying incidents by-value (i.e providing the actual values for each feature as described above), you can also specify incidents by-reference. This is useful, for example, if you maintain a live database of incidents along with the Network Analyst service. For instance, the Transportation Department of a city could provide a Network Analyst service containing information about recent traffic accidents. In such cases, the application does not need to know the actual details about each incident. All it needs to do is set up a layer definition specifiying which incidents should be included in the analysis.

Once you have a setup a layer definition identifying the features you wish to use, you can use it to specify incidents using setIncidentsWithLayerDefinition: method on an AGSClosestFacilityTaskParameters object

Tavel Direction

You can choose to find facilities by accumulating impedance in the direction away from the facility towards the incident or in the reverse direction towards the facility from the incident. On a network with one-way restrictions and different impedances based on direction of travel, this would result in different results. The direction you should choose depends on the nature of your analysis.

The closest facility for a fire response, for example, should be calculated away from the fire station towards the fire since it is imperative to get fire trucks and personnel quickly to the location of the fire. Alternatively, the closest facility for a traffic accident should choose the opposite direction since the urgent part of the trip for an incoming patient is going to the hospital.

params.travelDirection = AGSNATravelDirectionToFacility ;

Barriers

Barriers represent ad-hoc restrictions that must be taken into consideration when finding facilities. A barrier can specify a set of roads or a region that must be completely avoided. For example, a bridge that may be closed due to construction work. Some barriers may permit travel through them albeit at an added cost. For example, an accident on a freeway may temporarily slow down the traffic. This can be represented by a barrier that allows travel along the freeway but increases the travel time required.

Just as with facilities and incidents, there are two ways to specify barriers:

Features

You can create an array of AGSGraphic objects representing barrier features. You need to provide these features a geometry representing the location and shape of the barrier. The geometry can be a Point, Polyline, or a Polygon. You can then assign these barrier features to the parameter object using setPointBarriersWithFeatures:, setPolylineBarriersWithFeatures: , or setPolygonBarriersWithFeatures: depending upon the type of geometry that was assigned to the feature.

NoteNote:

You need to create separate arrays for point, polyline, and polygon barriers.

A barrier can also have additional attributes depending upon how the Closest Facility layer in the Network Analyst service is configured. These attributes are listed in the Network Analysis Classes -> Class Name:Barriers, PolylineBarriers, PolygonBarriers section of the Services Directory. Attributes may be of type input (specified by the client), output (returned by the server), or both.

Learn more about Barriers and the attributes supported by point, polyline, and polygon barriers

Layer Definition

Apart from specifying barriers by-value (i.e providing the actual values for each feature as described above), you can also specify barriers by-reference. This is useful when you already have a set of well known or commonly used barriers stored along with the Network Analyst service. For instance, the Transportation Department of a city could provide a Network Analyst service containing information about barriers representing the most recent traffic conditions, construction work, etc. In such cases, the application does not need to know the actual details about each barrier. All it needs to do is set up a layer definition specifiying which barriers should be included in the analysis.

Once you have a setup a layer definition identifying the features you wish to use, you can use it to specify barriers using setPointBarriersWithLayerDefinition:, setPolylineBarriersWithLayerDefinition:, or setPolygonBarriersWithLayerDefinition: methods on an AGSClosestFacilityTaskParameters object

U-Turn policy

You can specify if U-turns are allowed everywhere (AGSNAUTurnAllowBacktrack), nowhere (AGSNAUTurnNoBacktrack), only at dead ends (AGSNAUTurnAtDeadEndsOnly), or only at intersections and dead ends (AGSNAUTurnAtDeadEndsAndIntersections). Allowing U-turns implies the vehicle can turn around at a junction and double back on the same street.

Learn more about U-turns

The following code snippet allows U-turns only at dead ends and intersections :

params.uTurns = AGSNAUTurnAtDeadEndsAndIntersections ;

Driving Directions

The task can also return turn-by-turn driving directions for the route if you enable returnDirections on AGSClosestFacilityTaskParameters. You can specify the distance units to use (Miles, Kilometers, etc) through the directionsLengthUnits property and, depending upon the languages supported by the service, you can also specify which language to use through the directionsLanguage property.

The following code snippet requests for driving directions in French and using Kilometers as the distance units:

params.returnDirections = YES;
params.directionsLanguage = @"fr_FR";
params.directionsLengthUnits = AGSNAUnitKilometers;

Result options

Line options

You can choose to generate straight lines or true shape lines between the incidents and facilities by setting the outputLines property on AGSClosestFacilityTaskParameters. Lines can be straight or true shape. Straight lines are based on " as the crow flies" directions whereas true shape lines follow the underlying transportation network.

Geometry options

You can request for the geometry of the routes to be generalized by modifying the outputGeometryPrecision and outputGeometryPrecisionUnits properties on AGSClosestFacilityTaskParameters. Generalizing a geometry removes vertices that add more detail beyond the precision you specify. Removing vertices makes the geometry smaller thus saving time needed to transfer the geometry over the network and also faster to draw as a map graphic.

You can also request the geometry to be projected into a coordinate system of your choice by setting the outSpatialReference property of AGSClosestFacilityTaskParameters. This may be useful if the spatial reference of your map is different from the spatial reference of the service. If you do not specify an outSpatialReference , the geometries are returned in the spatial reference of the service by default.

Finding the closest facility

Once you have set up the input parameters, finding the closest facility is as simple as invoking solveClosestFacilityWithParameters: on AGSClosestFacilityTask and passing in the AGSClosestFacilityTaskParameters object to use in the calculation.

NSOperation* op = [cfTask solveClosestFacilityWithParameters:params];
The method returns an NSOperation object that you can use to cancel the operation if, for instance, the user is no longer interested in the results of the task.

Retrieving results and Handling errors

The Closest Facility task informs its delegate when operations complete successfully or when errors are encountered. To get results from the task and to properly handle any errors, you must set one of your classes as the task's delegate. You do this by making your class (typically the view controller which uses the task) adopt the AGSClosestFacilityTaskDelegate protocol.

@interface MyViewController : UIViewController <AGSClosestFacilityTaskDelegate> {  
  ... 
}

An instance of your class must also be set as the task's delegate. This will allow the task to invoke methods on your class in response to operations that the task performs.

cfTask.delegate = self;

Finally, your class should implement one ore more methods defined in the protocol which pertain to the operation being performed. There are a pair of methods for every type of operation - one for success and the other for failure. For instance, the delegate should implement closestFacilityTask:operation:didSolveClosestFacilityWithResult: method for when the operation completes successfully. Results are passed to the delegate method as an AGSClosestFacilityTaskResult object.

- (void) closestFacilityTask:(AGSClosestFacilityTask*)closestFacilityTask operation:(NSOperation*)op didSolveClosestFacilityWithResult:(AGSClosestFacilityTaskResult*) closestFacilityTaskResult{
	//process the results
}

An AGSClosestFacilityTaskResult object contains an array of AGSClosestFacilityResult objects representing the routes that were calculated between incidents and facilities. Each AGSClosestFacilityResult object contains

The AGSClosestFacilityTaskResult object also contains an array of messages providing information about any warnings or errors encountered while finding facilities.

NoteNote:

It is a good practice to inspect the messages, atleast during developement, as it could help troubleshoot any potential problems.

The delegate should also implement the closestFacilityTask:operation:didFailSolveWithError: method in order to be informed when an error is encountered. The error is passed into the method as an NSError object.

- (void) closestFacilityTask:(AGSClosestFacilityTask*)closestFacilityTask operation:(NSOperation*)op didFailSolveWithError:(NSError*) error{
  NSLog(@"Error: %@",error); 
}

12/7/2011