Arcpy: Polygons to Centroids (within Polygons, and with all Attributes)

Here’s a fairly simple python script that creates centroid points (constrained to fall within polygons) and attaches all the attributes (plus a link field, “ORIG_ID”) from the polygons to the centroids. Notably, it uses cursors to read raw geometry objects – no geoprocessing tools (except CreateFeatureclass). Also, while Advanced licensees may use the Feature to Point tool for this procedure, the method presented here is completely free, using no extensions or non-basic licensing – I don’t know of another free way to achieve this in ArcGIS. Wouldn’t it be easier to run a spatial join on the points to attach the attributes? Yes, but spatial join is slow. In my test of 1000 polygons, the cursor method completed in about 2 seconds, while adding a spatial join call extended the run time to more than 10 seconds. This could mean big savings on large datasets.

  1. # import libraries
  2. import arcpy, os
  3. # set input/output parameters
  4. polyFC = arcpy.GetParameterAsText(0)
  5. outCentroids = arcpy.GetParameterAsText(1)
  6. # set overwrite environment
  7. arcpy.env.overwriteOutput = True
  8. # if the output file does not exist, create it. Add “ORIG_ID” field.
  9. if not arcpy.Exists(outCentroids):
  10.     arcpy.CreateFeatureclass_management(os.path.dirname(outCentroids),
  11.                                     os.path.basename(outCentroids),
  12.                                     “POINT”,
  13.                                     polyFC,
  14.                                     “”,
  15.                                     “”,
  16.                                     polyFC)
  17.     arcpy.AddField_management(outCentroids,‘ORIG_ID’‘LONG’)
  18. # create an InsertCursor containing all the fields in ourCentroids, which are all the fields in polyFC plus “ORIG_ID”
  19. iCursor = arcpy.da.InsertCursor(outCentroids, [‘*’])
  20. # read all features in polyFC
  21. with arcpy.da.SearchCursor(polyFC, [“SHAPE@”,‘*’,‘OID@’]) as sCursor:
  22.     for row in sCursor:
  23.          # create an array to hold a new, modified row
  24.         rowArray = []
  25.          # read all the fields except “SHAPE@”
  26.         for fieldnum in range(1,len(row)):
  27.             # if this is the 2nd field [i.e. SHAPE], replace it with the centroid coordinates
  28.             if fieldnum == 2:
  29.                 rowArray.append(row[0].centroid)
  30.             else:
  31.                 rowArray.append(row[fieldnum])
  32.         # write the new row to the cursor
  33.         iCursor.insertRow(rowArray)
  34. del iCursor

Looping through the fields in this way (lines 29 – 36) feels cumbersome – if anyone knows a better way to do this, please let me know!

————————————————-

EDIT (January 19, 2016): Following some discussion on LinkedIn’s GIS and Geography group regarding this post, here is an example of why you must use the SHAPE@ token (and Polygon.centroid) rather than either the SHAPE@XY or SHAPE@TRUECENTROID tokens to guarantee that the point falls inside the given polygon:

… points = []
… with arcpy.da.SearchCursor(“MY_FEATURE_LAYER”,[‘SHAPE@’,’Shape@XY’,’SHAPE@TRUECENTROID’]) as cursor:
…   for row in cursor:
…     points.append(arcpy.PointGeometry(row[0].centroid)) # SHAPE@
…     points.append(arcpy.PointGeometry(arcpy.Point(row[1][0],row[1][1]))) # SHAPE@XY
…     points.append(arcpy.PointGeometry(arcpy.Point(row[2][0],row[2][1]))) # SHAPE@TRUECENTROID
… arcpy.CopyFeatures_management(points, r’in_memory\mypoints’)

centroid_tokens

 

Advertisements

11 thoughts on “Arcpy: Polygons to Centroids (within Polygons, and with all Attributes)

  1. Ken Carrier

    I think there is already a tool to do this, but thank you for sharing your code.

    arcpy.FeatureToPoint_management(in_features,out_feature_class)

    Reply
      1. Ken Carrier

        You mentioned spatial join being slow have you considered exporting both featureclasses into in_memory workspace, then performing spatial join? Once the join is complete in_memory then just use featureclass to featureclass to output result. Your method seems solid considering it works around the advanced licensing, just trying to think of alternatives. The only other way I can think to improve on the existing code is again read both feature classes into memory and the cursors will run even faster because you are not writing to disk each time an operation occurs.

        Reply
        1. dkwiens Post author

          Thanks for your thoughtful response. I haven’t tried forcing the spatial join through ‘in_memory’, but that may speed things up. Honestly, an extra 8 seconds isn’t the end of the world for my data (generally not “Big Data”). This was more an exploration into arcpy cursors and seeing how much of hassle it would be to remake the Feature to Point tool. Also, I didn’t do any extensive testing against spatial join – that may be a reasonable alternative with other datasets.

          Reply
  2. Ken Carrier

    FeatureToPoint_management(in_features, out_feature_class, {point_location})
    Creates a feature class containing points generated from the representative
    locations of input features.

    INPUTS:
    in_features (Feature Layer):
    The input features that can be multipoint, line, polygon, or annotation.
    point_location {Boolean}:
    Specifies whether to use representative centers of input features or locations contained by input features as the output point locations.

    * CENTROID—Uses the representative center of an input feature as its output point location. This is the default. This point location may not always be contained by the input feature.

    * INSIDE—Uses a location contained by an input feature as its output point location.

    OUTPUTS:
    out_feature_class (Feature Class):
    The output point feature class.

    Reply
  3. dkwiens Post author

    Hi Jon,

    Thanks for your comment.

    I’m not sure I understand. Both Model Builder and Python are used to automate tasks in ArcGIS. Model Builder is used when you want to avoid using Python, while Python is much more flexible than Model Builder (there are lots of things you can do in Python that you can’t do in Model Builder).

    The short answer is: No. If you already have a Python script (as above), then use it rather than making something new in Model Builder. And, even if you don’t have a Python script available, if you know Python, use Python. Never use Model Builder if you know Python.

    Reply
    1. Jon

      Thanks for clarifying that I am new with GIS. In my class I was using Model builder as a precursor to learning Python in the course. Yes it makes total since to use python straight out if you have the script. Very interesting what you did, it must have been cool to watch it draw when you ran the script.

      Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s