Using Feature Layers

The feature layer is the primary concept for working with features in a GIS.

Users create, import, export, analyze, edit, and visualize features, i.e. “entities in space” as feature layers.

Feature layers can be added to and visualized using maps. They act as inputs to and outputs from feature analysis tools.

Feature layers are created by publishing feature data to a GIS, and are exposed as a broader resource (Item) in the GIS. Feature layer instances can be obtained through the layers attribute on feature layer collection Items in the GIS. A feature layer collection is a collection of feature layers and tables, with the associated relationships among the entities. A feature layer collection is backed by a feature service in a web GIS.

Accessing Feature Layers

Feature layer collection items are available as content in the GIS. You can search the GIS for feature layer collection items, or get them using their item id.

Feature layers are available through the layers attribute on feature layer collection Items in the GIS.

Searching the GIS for feature layers

You can search the GIS for feature layer collections by specifying the item type as 'Feature Layer Collection' or 'Feature Layer'.

Note: A feature layer collection can be considered a type of feature layer such as a group feature layer. Hence, you can specify the item type as 'Feature Layer' and still get back feature layer collection items as results.

The examples below will clarify this further:

In [1]:
# Establish a connection to your GIS.
from arcgis.gis import GIS
from IPython.display import display
gis = GIS() # anonymous connection to www.arcgis.com
In [2]:
# Search for 'USA major cities' feature layer collection
search_results = gis.content.search('title: USA Major Cities and owner:esri',
                                    'Feature Layer')

# Access the first Item that's returned
major_cities_item = search_results[0]

major_cities_item
Out[2]:
USA Major Cities
This layer presents the locations of cities within United States with populations of 10,000 or greater (based on Census 2000 figures), all state capitals, and the national capital.Feature Layer Collection by esri
Last Modified: June 30, 2013
0 comments, 888,091 views

Note that the major_cities_item is a 'Feature Layer Collection' item. Since this item is a Feature Layer Collection, accessing the layers property will give us a list of FeatureLayer objects.

In [3]:
major_cities_layers = major_cities_item.layers
major_cities_layers
Out[3]:
[<FeatureLayer url:"http://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/USA_Major_Cities/FeatureServer/0">]

Accessing feature layers using item id

Feature layer collection items are available as content in the GIS. You can get them using their item id, and query their layers property to get to the feature layers:

In [4]:
freeways = gis.content.get('91c6a5f6410b4991ab0db1d7c26daacb')
freeways
Out[4]:
USA Freeway System
This layer presents rural and urban interstate highways.Feature Layer Collection by esri
Last Modified: December 08, 2016
0 comments, 843,708 views

Since freeways is a Feature Layer Collection item, accessing the layers property will give us a list of FeatureLayer objects. This item has two layers:

In [5]:
freeways.layers 
Out[5]:
[<FeatureLayer url:"http://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/USA_Freeway_System/FeatureServer/1">,
 <FeatureLayer url:"http://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/USA_Freeway_System/FeatureServer/2">]

The code below cycles through the layers and prints their names:

In [6]:
for lyr in freeways.layers:
    print(lyr.properties.name)
USA Freeway System (over 1:500k)
USA Freeway System (below 1:500k)

Accessing feature layers and tables from feature services

A feature service serves a collection of feature layers and tables, with the associated relationships among the entities. It is represented by arcgis.features.FeatureLayerCollection in the ArcGIS Python API.

Instances of FeatureLayerCollection can be constructed using a feature service url, as shown below:

In [7]:
from arcgis.features import FeatureLayerCollection
In [8]:
fs_url = 'http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/SanFrancisco/311Incidents/FeatureServer'
sanfran = FeatureLayerCollection(fs_url)

The collection of layers and tables in a FeatureLayerCollection can be accessed using the layers and tables properties respectively:

In [9]:
sanfran.layers
Out[9]:
[<FeatureLayer url:"http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/SanFrancisco/311Incidents/FeatureServer/0">]

Tables represent entity classes with uniform properties. In addition to working with “entities with location” as features, the GIS can also work with non-spatial entities as rows in tables. Working with tables is similar to working with feature layers, except that the rows (Features) in a table do not have a geometry, and tables ignore any geometry related operation.

The sanfran feature layer collection also has a table that can be obtained using its tables property:

In [10]:
sanfran.tables
Out[10]:
[<Table url:"http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/SanFrancisco/311Incidents/FeatureServer/1">]

Accessing feature layers from a feature layer url

Instances of FeatureLayers can also be constructed using a url to the REST endpoint of a feature layer:

In [11]:
from arcgis.features import FeatureLayer
In [12]:
lyr_url = 'http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/SanFrancisco/311Incidents/FeatureServer/0'

layer = FeatureLayer(lyr_url)
layer
Out[12]:
<FeatureLayer url:"http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/SanFrancisco/311Incidents/FeatureServer/0">

Properties of FeatureLayer

In this section, let us take a closer look at the properties of a FeatureLayer object. We will use the major_cities_layers object created earlier

In [13]:
feature_layer = major_cities_layers[0]
feature_layer
Out[13]:
<FeatureLayer url:"http://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/USA_Major_Cities/FeatureServer/0">

The properties field on a FeatureLayer object provides a dictionary representation of all its properties. However you can access individual properties as fields as well:

In [14]:
feature_layer.properties.extent
Out[14]:
{
  "xmax": -7656889.54512499,
  "xmin": -17608123.3895845,
  "ymax": 9568526.08684907,
  "ymin": 2237818.89912024,
  "spatialReference": {
    "latestWkid": 3857,
    "wkid": 102100
  }
}

The capabilities property is useful to know what kinds of edits and operations be performed on the feature layer

In [15]:
feature_layer.properties.capabilities
Out[15]:
'Query'

You can access the rendering information from the drawingInfo property

In [16]:
feature_layer.properties.drawingInfo.renderer.type
Out[16]:
'classBreaks'

Querying feature layers

Querying is a powerful operation that can be performed on a FeatureLayer object. Let is take a closer look here. To write meaningful queries, we need to know the names of fields present in the layer. This can be determined by calling the fields property:

In [17]:
for f in feature_layer.properties.fields:
    print(f['name'])
FID
NAME
CLASS
ST
STFIPS
PLACEFIP
CAPITAL
AREALAND
AREAWATER
POP_CLASS
POP2000
POP2007
WHITE
BLACK
AMERI_ES
ASIAN
HAWN_PI
OTHER
MULT_RACE
HISPANIC
MALES
FEMALES
AGE_UNDER5
AGE_5_17
AGE_18_21
AGE_22_29
AGE_30_39
AGE_40_49
AGE_50_64
AGE_65_UP
MED_AGE
MED_AGE_M
MED_AGE_F
HOUSEHOLDS
AVE_HH_SZ
HSEHLD_1_M
HSEHLD_1_F
MARHH_CHD
MARHH_NO_C
MHH_CHILD
FHH_CHILD
FAMILIES
AVE_FAM_SZ
HSE_UNITS
VACANT
OWNER_OCC
RENTER_OCC

The query method has a number of parameters that allow you to refine and transform the results. Since the processing is performed on the server, this operation is not restricted by the capacity of the client computer.

For instance, let us select all the cities whose population in the year 2007 was greater than 1 million. Instead of returning all the fields, let us get only population related fields

In [18]:
query_result1 = feature_layer.query(where='POP2007>1000000', 
                                    out_fields='WHITE,BLACK,MULT_RACE,HISPANIC')
len(query_result1.features)
Out[18]:
9
In [19]:
query_result1.fields
Out[19]:
[{'alias': 'WHITE',
  'defaultValue': None,
  'domain': None,
  'name': 'WHITE',
  'sqlType': 'sqlTypeInteger',
  'type': 'esriFieldTypeInteger'},
 {'alias': 'BLACK',
  'defaultValue': None,
  'domain': None,
  'name': 'BLACK',
  'sqlType': 'sqlTypeInteger',
  'type': 'esriFieldTypeInteger'},
 {'alias': 'MULT_RACE',
  'defaultValue': None,
  'domain': None,
  'name': 'MULT_RACE',
  'sqlType': 'sqlTypeInteger',
  'type': 'esriFieldTypeInteger'},
 {'alias': 'HISPANIC',
  'defaultValue': None,
  'domain': None,
  'name': 'HISPANIC',
  'sqlType': 'sqlTypeInteger',
  'type': 'esriFieldTypeInteger'}]

If we are only interested in the count, we could save bandwidth by setting the return_count_only to True

In [20]:
feature_layer.query(where='POP2007>1000000', return_count_only=True)
Out[20]:
9

Querying features using a different spatial reference

In [21]:
query_result1.spatial_reference
Out[21]:
{'latestWkid': 3857, 'wkid': 102100}

By default, the query results are in the same spatial reference as the source layer. However you can use the out_sr parameter to reproject the result into a desired spatial reference. The projection happens on the server and on all the resulting features.

In the example above, we obtained data in wkid:3857, a well known id for 'Web Mercator' projection. We can observe how the coordinates look like below:

In [22]:
query_result1.features[0].geometry
Out[22]:
{'x': -13040671.540682055, 'y': 3866078.7598941037}

The coordinates are in projected coordinate system as expected. If we wish to have this data in latitude and longitude instead, we could do so by changing the out_sr to wkid:4326

FeatureSet properties

As seen previously, a FeatureSet is returned by a query() operation. The FeatureSet object packs a bunch of useful properties that help us discern useful information about the features under access

One of the important properties is the spatial_reference as you saw earlier. Below, we are using the same query_result1 FeatureSet from earlier query operation.

In [23]:
query_result1.spatial_reference
Out[23]:
{'latestWkid': 3857, 'wkid': 102100}

One of the most powerful operation on a FeatureSet is accessing the features not as Feature objects, but as pandas dataframe objects. The df property, returns a dataframe object:

In [24]:
query2 = feature_layer.query(where="POP2007>1000000")
query2.df
Out[24]:
AGE_18_21 AGE_22_29 AGE_30_39 AGE_40_49 AGE_50_64 AGE_5_17 AGE_65_UP AGE_UNDER5 AMERI_ES AREALAND ... POP2000 POP2007 POP_CLASS RENTER_OCC ST STFIPS VACANT WHITE geometry.x geometry.y
FID
445 85983 176567 210745 174023 154166 211385 128008 82523 7543 324.341 ... 1223400 1301514 10 227411 CA 06 18998 736207 -1.304067e+07 3.866079e+06
530 80898 179201 222468 185726 163522 267919 106795 114516 26696 474.862 ... 1321045 1502129 10 183164 AZ 04 29998 938853 -1.247628e+07 3.965648e+06
590 226327 522360 644779 508091 454823 695335 357129 285976 29412 469.072 ... 3694820 3908521 10 783530 CA 06 62294 1734036 -1.317755e+07 4.040378e+06
968 72341 194619 208079 157720 137944 216791 102301 98785 6472 342.542 ... 1188580 1259459 10 256498 TX 48 32284 604209 -1.077430e+07 3.868725e+06
1069 70941 143970 177249 158471 147996 234211 119362 92446 9584 407.557 ... 1144646 1259735 10 169775 TX 48 27648 774708 -1.096638e+07 3.433243e+06
1297 117621 288621 324687 276105 245874 375861 164065 160797 8568 579.413 ... 1953631 2109413 10 389204 TX 48 64064 962610 -1.061801e+07 3.473108e+06
2566 172620 431648 479483 384230 369392 541318 298803 218522 10290 227.131 ... 2896016 2921775 10 597063 IL 17 90940 1215315 -9.760422e+06 5.137143e+06
3051 99908 182284 221352 206084 210731 285308 213722 98161 4073 135.089 ... 1517550 1475892 10 240438 PA 42 71887 683267 -8.365080e+06 4.865653e+06
3252 433490 1050181 1348263 1133497 1164721 1399391 937857 540878 41289 303.309 ... 8008278 8323732 10 2109292 NY 36 179324 3576385 -8.228506e+06 4.968814e+06

9 rows × 48 columns

Accessing the features as a dataframe makes if easier to analyze the data statistically.

Accessing Features from query results

In [25]:
query_geographic = feature_layer.query(where='POP2007>1000000', out_sr='4326')
query_geographic.features[0].geometry
Out[25]:
{'x': -117.14634560177157, 'y': 32.7795456430573}

We can execute the query() method on the first FeatureLayer object and get a FeatureSet. Let us query and access the first 10 features in this layer

In [26]:
major_cities_l1 = major_cities_layers[0]
major_cities_l1_fset = major_cities_l1.query(where= 'FID < 11')
type(major_cities_l1_fset)
Out[26]:
arcgis.features.feature.FeatureSet

Now, accessing the features property of the above FeatureSet object will provide us the individual point Features.

In [27]:
major_cities_l1_features = major_cities_l1_fset.features
len(major_cities_l1_features)
Out[27]:
10

Accessing Feature geometry and attributes

As mentioned earlier, the Feature object is a fine grained representation of spatial information. Two important properties of a Feature object are its geometry and attributes:

Let us display the geometry and attributes of the first feature

In [28]:
major_cities_l1_features[0].geometry
Out[28]:
{'x': -13154251.8779118, 'y': 4015371.3838001946}
In [29]:
major_cities_l1_features[0].attributes
Out[29]:
{'AGE_18_21': 3746,
 'AGE_22_29': 7991,
 'AGE_30_39': 9036,
 'AGE_40_49': 6232,
 'AGE_50_64': 5039,
 'AGE_5_17': 14319,
 'AGE_65_UP': 2849,
 'AGE_UNDER5': 6054,
 'AMERI_ES': 586,
 'AREALAND': 4.732,
 'AREAWATER': 0.105,
 'ASIAN': 1851,
 'AVE_FAM_SZ': 4.31,
 'AVE_HH_SZ': 3.93,
 'BLACK': 7508,
 'CAPITAL': ' ',
 'CLASS': 'City',
 'FAMILIES': 11334,
 'FEMALES': 28153,
 'FHH_CHILD': 2000,
 'FID': 1,
 'HAWN_PI': 464,
 'HISPANIC': 39945,
 'HOUSEHOLDS': 13972,
 'HSEHLD_1_F': 1130,
 'HSEHLD_1_M': 909,
 'HSE_UNITS': 14591,
 'MALES': 27113,
 'MARHH_CHD': 5060,
 'MARHH_NO_C': 2106,
 'MED_AGE': 25.6,
 'MED_AGE_F': 26.3,
 'MED_AGE_M': 24.9,
 'MHH_CHILD': 636,
 'MULT_RACE': 2640,
 'NAME': 'Paramount',
 'OTHER': 23040,
 'OWNER_OCC': 5999,
 'PLACEFIP': '55618',
 'POP2000': 55266,
 'POP2007': 57155,
 'POP_CLASS': 7,
 'RENTER_OCC': 7973,
 'ST': 'CA',
 'STFIPS': '06',
 'VACANT': 619,
 'WHITE': 19177}

Using feature collections

Similar to feature layers, feature collections can also be used to store features. With a feature collection, a service is not created to serve out feature data. Instead it is stored as json data with the item. Feature collections can be added to maps as layers, passed as input to feature analysis tools and queried for feature data.

Feature collections are shared in the GIS as items. Feature Collection Items can be searched by specifying 'Feature Collection' as the item_type.

Let us search for a feature collection published by Iowa Dept. of Transportation as an example:

In [30]:
search_fc = gis.content.search("title:AVL_Direct_FC", item_type='Feature Collection')
iowa_fc_item = search_fc[0]
iowa_fc_item
Out[30]:
AVL_Direct_FC
2016-12-16 12:24:14Feature Collection by IowaDOT_SODA
Last Modified: December 16, 2016
0 comments, 2,091,151 views

Accessing the layers property on a feature collection item returns a list of FeatureCollection objects

In [31]:
iowa_fc_item.layers
Out[31]:
[<FeatureCollection>]
In [32]:
iowa_fc = iowa_fc_item.layers[0]

You can call the query() method on a FeatureCollection object to get a FeatureSet.

In [33]:
iowa_fset = iowa_fc.query()

Once you have a FeatureSet object, you can access the features property to get a list of Feature objects as seen earlier

In [34]:
iowa_features = iowa_fset.features
iowa_features[0].geometry
Out[34]:
{'spatialReference': {'latestWkid': 3857, 'wkid': 102100},
 'x': -10264465.245318891,
 'y': 5228499.213465497}

Feedback on this topic?