<jupyter_start><jupyter_text>GeoPandas Demo: Get CountiesThis example demonstrates how to grab data from an ArcGIS MapService and pull it into a GeoPandas data frame.<jupyter_code>import requests
import pandas as pd
import geopandas as gpd

%matplotlib inline<jupyter_output><empty_output><jupyter_text>Fetching some dataWe'll tap into a NOAA map server to pull some state boundary features...* Build the request* Send the request, receive the response<jupyter_code>#Build the request and parameters to fetch county features
#  from the NOAA ArcGIS map server end point
stateFIPS = '37' #This is NC

url = 'https://nowcoast.noaa.gov/arcgis/rest/services/nowcoast/mapoverlays_political/MapServer/find'
params = {'searchText':stateFIPS,
          'contains':'true',
          'searchFields':'STATEFP',
          'sr':'',
          'layers':'2',
          'layerDefs':'',
          'returnGeometry':'true',
          'maxAllowableOffset':'',
          'geometryPrecision':'',
          'dynamicLayers':'',
          'returnZ':'false',
          'returnM':'false',
          'gdbVersion':'',
          'returnUnformattedValues':'false',
          'returnFieldName':'false',
          'datumTransformations':'',
          'layerParameterValues':'',
          'mapRangeValues':'',
          'layerRangeValues':'',
          'f':'json'}
#Fetch the data
response = requests.get(url,params)<jupyter_output><empty_output><jupyter_text>Examining the response* Convert the response to a JSON object* Examine its structure* Extract the `attributes` and `geometry` elements.<jupyter_code>#Convert to a JSON object (i.e. a dictionary)
respons_js = response.json()
#The 'results' object contains a record for each county returned, i.e., a feature
results = respons_js['results']
len(results)
#Within each item in the results object are the following items
results[0].keys()
#The 'attributes' item contains the feature attributes
results[0]['attributes']
#And the geometry object contains the shape
results[0]['geometry']<jupyter_output><empty_output><jupyter_text>Convert the elements to dataFrames* Creating a dataFrame from the Results object* "Exploding" the dictionary values in the `attributes` and `geometry` columns* Concatenating dataFrames lengthwise (adding columns)<jupyter_code>#Create a dataFrame from the results, 
#  keeping just the attributes and geometry objects
df = pd.DataFrame(results,columns=('attributes','geometry'))
df.head()
#Explode the dictionary values into fields
dfCounties = df['attributes'].apply(pd.Series)
dfGeom = df['geometry'].apply(pd.Series)
#Combine the two
dfAll = pd.concat((dfCounties,dfGeom),axis='columns')
dfAll.head()<jupyter_output><empty_output><jupyter_text>Converting the [ESRI] geometry coordinates to a [shapely] geometric featureThe `dfAll` dataframe now has all feature attributes and the geometry object stored in the `rings` column. * Exploring the 'rings' object* Exploring the `shapely` package: rings, polygons, and multipolygons* Using shapely to create features* Converting the dataFrame to geodataFrame* Plotting the output<jupyter_code>#Explore the values in the "ring" column, looking at the first row of data
rings = dfAll['rings'][0]
print ("There is/are {} ring(s) in the record".format(len(rings)))
print ("There are {} vertices in the first ring".format(len(rings[0])))
print ("The first vertex is at {}".format(rings[0][0]))<jupyter_output>There is/are 1 ring(s) in the record
There are 524 vertices in the first ring
The first vertex is at [-8608078.87744889, 4310879.947869732]<jupyter_text>So, the "ring" value in each row of our dataframe contains a *list* of rings, with each ring being a list of coordinates defining the vertices of our polyon. Usually the list of rings only includes one ring, the outer boundary of a single polygon. However, it's possible it contains more than one, e.g. the boundary of Hawaii.  Now we'll extract the first ring object from the ring list of the first record in our dataframe and convert it to a Shapely polygon object. To do this we need to import a few Shapely geometry class objects.<jupyter_code>#Import the shapely objects we'll need
from shapely.geometry import LinearRing
from shapely.geometry import Polygon
#Create a shapely polygon from the first ring 
ring = rings[0]        # Get the outer ring, in coordinates
r = LinearRing(ring)   # Convert coordinates to shapely ring object
s = Polygon(r)         # Convert shapely ring object to shapely polygon object
s.area                 # Show the area of the polygon<jupyter_output><empty_output><jupyter_text>Now that we've seen the proof of concept, we'll form a Python function that * takes a list of rings (i.e., the value of one row's `rings` field), * converts each ring item in this ring list into a Shapely LinearRing object, * converts *that* into a Shapely polygon object, adding each these polygons to a list, * and then constructs a Shapely MultiPolygon object from the list of polygons<jupyter_code>#A function to convert all rings into a Shapely multipolygon object
def polyFromRings(rings):
    #Import necessary Shapely classes
    from shapely.geometry import LinearRing, Polygon, MultiPolygon
    #Construct an empty list of polygons
    polyList = []
    #Compile a list of shapely ring objects and convert to polygons
    for ring in rings:
        #Construct a ring from the ring coordinates
        r = LinearRing(ring)
        #Convert the ring to a shapely polygon
        s = Polygon(r)
        #Add the polygon to the polyList
        polyList.append(s)
    #Convert the list of polyongs to a multipolygon object
    multiPoly = MultiPolygon(polyList)
    return multiPoly<jupyter_output><empty_output><jupyter_text>Now, we use Panda's `apply` method to apply the "polyFromRings" function above to each row's "ring" values.<jupyter_code>#Apply the function to each item in the geometry column
dfAll['geometry']=dfAll.apply(lambda x: polyFromRings(x.rings),axis='columns')<jupyter_output><empty_output><jupyter_text>Convert dataframe to a *geo*dataframeWith the rings successfuly converted to Shapely geometry objects, we can now "upgrade" our Pandas dataframe to a GeoPandas dataframe, capable of spatial analysis<jupyter_code>#Create a geodataframe from our pandas dataframe (the geometry column must exist)
gdf=gpd.GeoDataFrame(dfAll)
gdf.head()
#Set the projection (obtained from spatialReference column)
gdf.crs = {'init': 'epsg:3857'}
#Check the data types; note some should be fixed!
gdf.dtypes
#Convert the `ALAND` and `AWATER` to floating point values
gdf['ALAND']=gdf['ALAND'].astype('double')
gdf['AWATER']=gdf['AWATER'].astype('double')
gdf.dtypes
#Use familiar Pandas operation to select a feature and 
gdf[gdf['NAME'] == 'Durham'].plot();<jupyter_output><empty_output><jupyter_text>Saving the dataWe can save the attributes to CSV file or save the feature class to a shapefile<jupyter_code>#Save our attribute data to a shapefile
gdf.to_csv("counties_{}.csv".format(stateFIPS))<jupyter_output><empty_output><jupyter_text>Saving data to a shapefile is a bit more finicky. In particular, we need to remove the old "rings" field in the geodataframe.<jupyter_code>#Delete the 'rings' column from our geodataframe
gdf.drop('rings',axis='columns',inplace=True)
#Write the data to a file
gdf.to_file(driver='ESRI Shapefile',filename="./data/NC_Counties.shp")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09315254237288136, 'fraction_numerical': 0.006779661016949152, 'mean_word_length': 4.97246963562753, 'pattern_counts': {'":': 0, '<': 44, '<?xml version=': 0, '>': 44, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '304595', 'license': 'CNRI-Python', 'n_tokens_mistral': 2253, 'n_tokens_neox': 2135, 'n_words': 810, 'path': '2-GetCounties-Documented.ipynb', 'repo_name': 'CieloBM/GeoPandas', 'size': '7375'}