Skip To Content ArcGIS for Developers Sign In Dashboard

ArcGIS API for Python

Download the samples Try it live

Data Visualization - Construction permits, part 1/2

Overview

One indicator of a region's growth is the number of permits issued for new construction. Exploring and analyzing permit activity can help regional planners ensure that development occurs in accordance to the area's long-term goals. One area that has recently experienced rapid growth is Montgomery County, Maryland, a suburban county near Washington, D.C. County planners want to observe spatial and temporal growth trends, find out why certain areas are growing faster than others, and communicate key information about the county's growth to the public.

In this notebook, you'll explore Montgomery County permit data. First, you'll add the permit data from ArcGIS Living Atlas of the World. You'll explore the data and become familiar with exactly what kind of information it contains. Then, you'll analyze the data to detect patterns and find out why growth is occurring. Once you've gathered your findings from your exploration and analysis, you'll share your work online.

Explore the data

To better understand trends in permit activity in Montgomery County, you'll add a dataset of permits issued since 2010. Before you begin your analysis, however, it's important to explore your data and understand what it shows and does not show. You'll familiarize yourself with the data's attributes, sort the data by type, and visualize spatial and temporal trends. In doing so, you'll gain context for your analysis and know exactly which questions you still need to ask to find out why, where, and when growth is occurring.

Connect to your ArcGIS online organization.

In [1]:
from arcgis.gis import GIS
import pandas as pd

from arcgis.features import GeoAccessor, GeoSeriesAccessor
In [2]:
agol_gis = GIS()

Search for the Commercial Permits since 2010 layer. You can specify the owner's name to get more specific results. To search for content from the Living Atlas, or content shared by other users on ArcGIS Online, set outside_org=True.

In [3]:
data = agol_gis.content.search('title: Commercial Permits since 2010 owner: rpeake_LearnGIS', 'Feature layer', 
                               outside_org=True)
data[0]
Out[3]:
Commercial Permits since 2010
Commercial building permits issued in Montgomery County, Maryland, since 2010.Feature Layer Collection by rpeake_LearnGIS
Last Modified: December 09, 2017
0 comments, 846 views

Get the first item from the results.

In [4]:
permits = data[0]

Since the item is a Feature Layer Collection, accessing the layers property gives us a list of FeatureLayer objects. The permit layer is the first layer in this item. Visualize this layer on a map of Montgomery County, Maryland.

In [5]:
permit_layer = permits.layers[0]
In [6]:
permit_map = agol_gis.map('Montgomery County, Maryland', zoomlevel=9)
permit_map
Out[6]:

You can add a number of different layer objects such as FeatureLayer, FeatureCollection, ImageryLayer, MapImageLayer to the map by calling the add_layer() method.

In [7]:
permit_map.add_layer(permit_layer)

Data Exploration

Now that you've added the permit data, you'll explore its contents. Geographic data doesn't only contain information about location; it can also include other attributes not seen on a map.

Convert the layer into a spatially-enabled dataframe to explore these attributes.

In [8]:
permit_layer
Out[8]:
<FeatureLayer url:"https://services2.arcgis.com/j80Jz20at6Bi0thr/arcgis/rest/services/Commercial_Permits_since_2010/FeatureServer/0">
In [9]:
sdf = pd.DataFrame.spatial.from_layer(permit_layer)

tail() method gives the last 5 rows of the dataframe.

In [10]:
sdf.tail()
Out[10]:
Added_Date Address Applicatio BldgAreaNu Building_A City DeclValNu DeclValNu2 Declared_V Descriptio ... Pre_direct SHAPE State Status Street_Nam Street_Num Street_Suf Use_Code Work_Type ZIP_code
11219 2014-01-31 1015 SPRING ST COMMERCIAL BUILDING 707.91 707.91 SILVER SPRING 42000.0 42000.0 $42,000.00 Silver Spring Enterprise Zone\n\nAdd exterior ... ... {"x": -8574668.7047, "y": 4721607.997599997, "... MD Stop Work SPRING 1015 ST BUSINESS BUILDING ADD 20910
11220 2014-02-11 26100 WOODFIELD RD COMMERCIAL BUILDING 0.00 0 DAMASCUS 5875.0 5875.0 $5,875.00 PYLON SIGN ... {"x": -8594080.2264, "y": 4762636.6635000035, ... MD Stop Work WOODFIELD 26100 RD BUSINESS BUILDING CONSTRUCT 20872
11221 2014-02-20 10520 MONTROSE AVE COMMERCIAL BUILDING 728.00 728 BETHESDA 31000.0 31000.0 $31,000.00 Remodeling a one story building with walk-out ... ... {"x": -8582314.4798, "y": 4725770.635600001, "... MD Stop Work MONTROSE 10520 AVE ASSEMBLY BUILDING ADD 20814
11222 2014-03-06 8500 RIVER RD COMMERCIAL BUILDING 472.02 472.02 BETHESDA 1000.0 1000.0 $1,000.00 TOTAL OF 17 GROUPED TENTS FOR 2014 QUICKEN LOA... ... {"x": -8591206.0439, "y": 4721680.315399997, "... MD Stop Work RIVER 8500 RD COMMERCIAL MISCELLANEOUS STRUC CONSTRUCT 20817
11223 2014-03-10 8500 RIVER RD COMMERCIAL BUILDING 8461.55 8461.55 BETHESDA 1.0 1.0 $1.00 1 GRANDSTAND & MULTIPLE PLATFORMS FOR 2014 QUI... ... {"x": -8591206.0439, "y": 4721680.315399997, "... MD Stop Work RIVER 8500 RD COMMERCIAL MISCELLANEOUS STRUC CONSTRUCT 20817

5 rows × 26 columns

The permit data contains a long list of attributes. Some attributes have self-explanatory names, while others may have names that can be difficult to understand without context. The list of attributes can be obtained using the columns of the dataframe.

In [11]:
sdf.columns
Out[11]:
Index(['Added_Date', 'Address', 'Applicatio', 'BldgAreaNu', 'Building_A',
       'City', 'DeclValNu', 'DeclValNu2', 'Declared_V', 'Descriptio', 'FID',
       'Final_Date', 'Issue_Date', 'Location', 'Permit_Num', 'Post_direc',
       'Pre_direct', 'SHAPE', 'State', 'Status', 'Street_Nam', 'Street_Num',
       'Street_Suf', 'Use_Code', 'Work_Type', 'ZIP_code'],
      dtype='object')
In [12]:
sdf.describe().T
Out[12]:
count mean std min 25% 50% 75% max
BldgAreaNu 11224.0 9241.778777 3.950484e+04 0.0 255.00 1537.0 4000.00 1.548205e+06
DeclValNu 11224.0 784736.035620 1.152229e+07 0.0 20000.00 74000.0 200000.00 1.129634e+09
DeclValNu2 11224.0 784736.035620 1.152229e+07 0.0 20000.00 74000.0 200000.00 1.129634e+09
FID 11224.0 5612.500000 3.240234e+03 1.0 2806.75 5612.5 8418.25 1.122400e+04
Permit_Num 11224.0 655806.112794 7.759285e+04 528631.0 587437.50 652073.5 722003.25 7.961930e+05
ZIP_code 11224.0 20848.988863 5.581882e+02 0.0 20832.00 20871.0 20901.00 2.177100e+04

Query the types of attributes and explore the data.

In [13]:
sdf.dtypes
Out[13]:
Added_Date    datetime64[ns]
Address               object
Applicatio            object
BldgAreaNu           float64
Building_A            object
City                  object
DeclValNu            float64
DeclValNu2           float64
Declared_V            object
Descriptio            object
FID                    int64
Final_Date    datetime64[ns]
Issue_Date    datetime64[ns]
Location              object
Permit_Num             int64
Post_direc            object
Pre_direct            object
SHAPE               geometry
State                 object
Status                object
Street_Nam            object
Street_Num            object
Street_Suf            object
Use_Code              object
Work_Type             object
ZIP_code               int64
dtype: object
In [14]:
sdf['Work_Type'].unique()
Out[14]:
array(['CONSTRUCT', 'ALTER', 'COMMERCIAL CHANGE OF USE',
       'RESTORE AND / OR REPAIR', 'ADD', 'BUILD FOUNDATION', 'INSTALL',
       'REPLACE', 'CONSTRUCT SHEETING/SHORING', 'FINAL ONLY AP',
       'REMOVE AND REPLACE', 'OCCUPY', 'DEMOLISH'], dtype=object)
In [15]:
sdf['Status'].unique()
Out[15]:
array(['Finaled', 'Issued', 'Open', 'Stop Work'], dtype=object)
In [16]:
sdf['Use_Code'].unique()
Out[16]:
array(['MULTI-FAMILY DWELLING', 'RESTAURANT', 'BUSINESS BUILDING',
       'MERCANTILE BUILDING', 'PLACE OF WORSHIP', 'ASSEMBLY BUILDING',
       'STORAGE BUILDING', 'GARAGE', 'INSTITUTIONAL BUILDING',
       'COMMERCIAL MISCELLANEOUS STRUC', 'INDUSTRIAL BUILDING',
       'EDUCATIONAL BUILDING', 'TOWER', 'SWIMMING POOL', 'FENCE', 'BANK',
       'SHED', 'MULTI-FAMILY SENIOR CITIZEN BL', 'RETAINING WALL',
       'TRAILER', 'HOSPITAL', 'BIOSCIENCE', 'TOWNHOUSE', 'HOTEL',
       'FACTORY', 'BOARDING HOUSE', 'SWIMMING POOL & FENCE',
       'UTILITY, MISCELLANEOUS', 'THEATER',
       'MULTIFAMILY DWELLING HIGH RISE', 'MULTIFAMILY DWELLING LOW RISE',
       'MISCELLANEOUS STRUCTURE', 'OWNERSHIP UNIT'], dtype=object)

Permits by Status

The groupby() method groups the rows per the column and does calculations, such as finding their counts, as shown in the following code.

In [17]:
permits_by_status = sdf.groupby(sdf['Status']).size()
permits_by_status
Out[17]:
Status
Finaled      5341
Issued       4696
Open          757
Stop Work     430
dtype: int64

There are only four permit statuses: Issued, Finaled, Open, and Stop Work. To visualize the number of permits for each status, you'll create a pie chart.

Since the dataframe attributes just show the count of status, you can consider any attribute to graph the status count.

In [18]:
%matplotlib inline
import matplotlib.pyplot as plt
In [19]:
plt.axis('equal') 
permits_by_status.plot(kind='pie', legend=False, label='Permits by Status');

The pie chart above shows the four permit statuses, with the size of each status determined by the number of permits. The vast majority of permits are either Issued or Finaled. Finaled permits are issued permits that have also had the requisite inspections performed.

It's helpful to visualize the spatial distribution of permit attributes on a map. You'll change the map so that each permit's symbol represents its status.

In [20]:
permits_by_status_map = agol_gis.map('Montgomery County, Maryland')
permits_by_status_map
Out[20]: