Analysis tools

This sample notebook shows how you can access the spatial analysis tools available via your ArcGIS Online organization. The documentation for the service is available at ArcGIS REST API: Spatial Analysis Service.

First, you would need to establish a connection to your organization which could be an ArcGIS Online organization or an ArcGIS Enterprise. To be able to run the code in this sample notebook, you would need to provide credentials of a user within an ArcGIS Online organization.

In [1]:
import pandas as pd
from arcgis.gis import GIS
import arcgis.network as network
import arcgis.features as features
import arcgis.features.use_proximity as use_proximity
import arcgis.features.find_locations as find_locations

user_name = 'arcgis_python'
password = 'P@ssword123'
my_gis = GIS('https://www.arcgis.com', user_name, password)

Find nearest

The Find Nearest task measures the straight-line distance, driving distance, or driving time from features in the analysis layer to features in the near layer, and copies the nearest features in the near layer to a new layer. Connecting lines showing the measured path are returned as well.

To get all possible values for the measurement_type parameter, you can call the retrieve_travel_modes() method of the network.RouteLayer object. The default value is StraightLine which will generate the Euclidean distance lines connecting the locations.

In [2]:
route_service = network.RouteLayer(my_gis.properties.helperServices.route.url, gis=my_gis)
print(sorted([i['name'] for i in route_service.retrieve_travel_modes()['supportedTravelModes']]))
['Driving Distance', 'Driving Time', 'Rural Driving Distance', 'Rural Driving Time', 'Trucking Distance', 'Trucking Time', 'Walking Distance', 'Walking Time']

In this example, we will find the distance from a point to a few other points in another feature collection. The input features parameter can be a URL to a feature service layer with an optional filter to select specific features, or a feature collection. In this example, we will construct a feature collection from a Python dictionary.

In [3]:
near_layer_fcol_def = {
    "layerDefinition": {        
        "geometryType": "esriGeometryPoint",
        "fields": [
            {
                "alias": "OBJECTID",
                "name": "OBJECTID",
                "type": "esriFieldTypeOID",
                "editable": False
            },
            {
                "alias": "NAME",
                "name": "NAME",
                "length": 255,
                "type": "esriFieldTypeString",
                "editable": True
            }            
        ]
    },
    "featureSet": {
        "geometryType": "esriGeometryPoint",
        "features": [
            {
                "geometry": {
                    "x": -13036019.880334152,
                    "y": 3864814.036269334,
                    "spatialReference": {
                        "wkid": 102100,
                        "latestWkid": 3857
                    }
                },
                "attributes": {
                    "NAME": "Point1",
                    "OBJECTID": 0
                }
            },
            {
                "geometry": {
                    "x": -13034185.391655311,
                    "y": 3863705.6993592004,
                    "spatialReference": {
                        "wkid": 102100,
                        "latestWkid": 3857
                    }
                },
                "attributes": {
                    "NAME": "Point2",
                    "OBJECTID": 1
                }
            }
        ]
    }
}

near_feat_col = features.FeatureCollection(near_layer_fcol_def)
In [4]:
analysis_layer_fcol_def = {
    "layerDefinition": {
        "geometryType": "esriGeometryPoint",
        "fields": [
            {
                "alias": "OBJECTID",
                "name": "OBJECTID",
                "type": "esriFieldTypeOID",
                "editable": False
            },
            {
                "alias": "NAME",
                "name": "NAME",
                "length": 255,
                "type": "esriFieldTypeString",
                "editable": True
            }
        ]
    },   
    "featureSet": {
        "geometryType": "esriGeometryPoint",
        "features": [
            {
                "geometry": {
                    "x": -13032694.869603751,
                    "y": 3865368.204724401,
                    "spatialReference": {
                        "wkid": 102100,
                        "latestWkid": 3857
                    }
                },
                "attributes": {
                    "NAME": "AnalysisPoint",
                    "OBJECTID": 0
                }
            }
        ]
    }
}

analysis_feat_col = features.FeatureCollection(analysis_layer_fcol_def)
In [5]:
car_mode = [i for i in route_service.retrieve_travel_modes()['supportedTravelModes'] 
            if i['name'] == 'Driving Time'][0]
result = use_proximity.find_nearest(analysis_feat_col, near_feat_col, measurement_type=car_mode,
                                    context={'outSR': {"wkid": 4326}})
Location "AnalysisPoint" in "Incidents" is on a soft-restricted network element.
Network elements with avoid-restrictions are traversed in the output (restriction attribute names: "Through Traffic Prohibited").
In [6]:
df = pd.DataFrame.from_records([i['attributes'] for i in 
                                result['connecting_lines_layer'].layer.featureSet.features])
df[['RouteName', 'Total_Kilometers', 'Total_Minutes']]
Out[6]:
RouteName Total_Kilometers Total_Minutes
0 AnalysisPoint - Point2 4.188868 8.945245
1 AnalysisPoint - Point1 6.295195 11.481706

Plan routes

The Plan Routes task determines how to efficiently divide tasks among a mobile workforce.

You provide the input, which includes a set of stops and the number of vehicles available to visit the stops, and the tool assigns the stops to vehicles and returns routes showing how each vehicle can reach their assigned stops in the least amount of time.

In this example, we will use features from a hosted feature service, apply a filter to it to retrieve only a few cities, and then find out the best sequence to travel through them.

In [7]:
sample_cities = my_gis.content.search('title:"USA Major Cities" type:Feature Service owner:esri', 
                                      outside_org=True)[0]
stops_cities = ['San Francisco', 'San Jose', 'Sacramento', 
                'Los Angeles', 'San Diego']

values = "'" + "', '".join(stops_cities) + "'"
stops_layer = {'url': sample_cities.layers[0].url, 
               'filter': "ST in ('CA', 'NV')  AND NAME IN ({0})".format(values)}
start_layer = {'url': sample_cities.layers[0].url, 
               'filter': "NAME = 'Fresno'"}

result = use_proximity.plan_routes(stops_layer=stops_layer, route_count=1, 
                                   max_stops_per_route=10, route_start_time=1413964800000,
                                   start_layer=start_layer, context={'outSR': {"wkid": 4326}})
Input field [OID] was not mapped to a field in the network analysis class "Orders".
Input field [OID] was not mapped to a field in the network analysis class "Depots".
Network elements with avoid-restrictions are traversed in the output (restriction attribute names: "Avoid Private Roads" "Through Traffic Prohibited").
In [8]:
df = pd.DataFrame.from_records([i['attributes'] for i in 
                                result['routes_layer'].layer.featureSet.features])
df[['RouteName', 'StopCount', 'TotalTime', 'Total_Kilometers']]
Out[8]:
RouteName StopCount TotalTime Total_Kilometers
0 Fresno - Route1 5 1214.285254 1846.648926

Create Drive-Time Areas

The Create Drive-Time Areas task creates areas that can be reached within a given drive time or drive distance. In this example, we will generate a drive time area of 15 min around the city of Riverside, CA.

In [9]:
sample_cities = my_gis.content.search('title:"USA Major Cities" type:Feature Service owner:esri', 
                                      outside_org=True)[0]
input_layer = {'url': sample_cities.layers[0].url, 
               'filter': "NAME = 'Riverside' AND ST = 'CA'"}

result = use_proximity.create_drive_time_areas(input_layer=input_layer, break_values=[15], 
                                               context={'outSR': {"wkid": 4326}})

When creating drive-time areas, the output polygons will be enriched with various demographics data. For brevity, we show just a few columns.

In [10]:
df = pd.DataFrame.from_records([i['attributes'] for i in 
                                result.layer.featureSet.features])
df[['Name', 'AnalysisArea', 'AGE_5_17', 'AGE_18_21', 'AGE_22_29', 'AGE_30_39', 'AGE_40_49', 'AGE_50_64', 'AGE_65_UP']]
Out[10]:
Name AnalysisArea AGE_5_17 AGE_18_21 AGE_22_29 AGE_30_39 AGE_40_49 AGE_50_64 AGE_65_UP
0 Riverside : 0 - 15 296.728821 56269 20764 31026 38638 35315 29665 23054

Connect Origins to Destinations

The Connect Origins to Destinations task measures the travel time or distance between pairs of points. You provide starting and ending points, and the tool returns a layer containing route lines, including measurements, between the paired origins and destinations.

In this example, we will find the travel distance between multiple cities which are loaded from a hosted feature service.

In [11]:
sample_cities = my_gis.content.search('title:"USA Major Cities" type:Feature Service owner:esri', 
                                      outside_org=True)[0]
origins_layer = {'url': sample_cities.layers[0].url, 
                 'filter': "ST = 'CA' AND NAME in ('Riverside', 'Los Angeles') "}
destinations_layer = {'url': sample_cities.layers[0].url, 
                      'filter': "ST = 'CA' AND NAME = 'Victorville' "}

result = use_proximity.connect_origins_to_destinations(origins_layer=origins_layer, 
                                                       destinations_layer=destinations_layer,
                                                       context={'outSR': {"wkid": 4326}})
Location "Los Angeles" in "Stops" is on a soft-restricted network element.
Network elements with avoid-restrictions are traversed in the output (restriction attribute names: "Avoid Private Roads" "Through Traffic Prohibited").
In [12]:
df = pd.DataFrame.from_records([i['attributes'] for i in 
                                result['routes_layer'].layer.featureSet.features])
df[['RouteName', 'Total_Kilometers', 'Total_Miles', 'Total_Minutes']]
Out[12]:
RouteName Total_Kilometers Total_Miles Total_Minutes
0 Los Angeles - Victorville 160.509180 99.73578 123.554199
1 Riverside - Victorville 83.619357 51.95866 55.716183

Feedback on this topic?