3

I am using ArcGis 9.3 and I need to split a large number of circles in quadrants. The circles corresponde to a 200m buffer of a point layer and, for each point, I need to split the buffer in 8 quadrants. Is there any ArcGis tool or extension that does this for all points/buffers at the same time?

  • 2
    So you you want to split each circle into 8 pie-like wedges? Is that correct? If not, can you provide a graphic of what you are after? – RyanDalton Apr 29 '14 at 20:30
6

I've written a script for 10.1/10.2, however you might see if it is possible to rewrite it for 9.3. I've almost never used arcgisscripting, but I guess it would be hard to implement the same without arcpy in 10.0+. Bearing Distance To Line GP tool I've used is available only in 10.0+, too.

If 9.3 is your only option, you could try to generate the lines with arcgisscripting in 9.3 (with the same logic as I've used with arcpy in the code below) and then use ET Geowizards Partition Polygons with Polylines which works similar to Feature To Polygon GP tool in ArcGIS. Another tool that looks promising is Points To Pie Segments from ET Geowizards.

If you will be able to get 10.1+ via evaluation license or if you upgrade, then you could use this script. All other users who find this post and are on 10.1+ could also benefit from using this script. This script assumes your source points you've used for buffering represent the centroid of the output buffer circles.

import arcpy, math

arcpy.env.overwriteOutput = True
arcpy.env.workspace = r"C:\GIS\Temp\test.gdb"
tempworkspace = arcpy.env.workspace
in_poly_fc = r"C:\GIS\Temp\test.gdb\CirclesFeatureClass"
clip_lines_fc = r"ClipLines"
sum_lines_fc = r"SumClipLines"

#converting an existing fc with circles to Geometry objects
geometries = arcpy.CopyFeatures_management(in_poly_fc,arcpy.Geometry())

for feature in geometries:
    #obtaining a centroid of the circle centroid via Geometry class attribute
    centroid_Point = feature.trueCentroid
    centroid_xy = []

    #obtaining XY coordinates of the centroid via Point class attribute
    centroid_xy.append(centroid_Point.X)
    centroid_xy.append(centroid_Point.Y)

    #obtaining the radius
    #have to add a small overhead value to make sure the radius end point will overshoot the polygon
    #otherwise may get undershooting lines which depends on the precision of the coordinates
    #and XY tolerance of the source data
    radius = math.sqrt(feature.area / math.pi) + 1

    #supply the list of angles for bearing
    bearing_angles = [0,45,90,135,180,225,270,315,360]

    #creating bearing angles table and adding fields
    bearing_table = arcpy.CreateTable_management(tempworkspace,"BearingDataTable")
    arcpy.AddField_management(bearing_table,"Xcoord","Double")
    arcpy.AddField_management(bearing_table,"Ycoord","Double")
    arcpy.AddField_management(bearing_table,"Bearing","Double")
    arcpy.AddField_management(bearing_table,"Distance","Double")

    #inserting all required lines constructed from centroid with the radius and bearing angle
    with arcpy.da.InsertCursor(bearing_table,["Xcoord","Ycoord","Bearing","Distance"]) as ins_cursor:
        for bearing_angle in bearing_angles:
            ins_cursor.insertRow((centroid_xy[0],centroid_xy[1],bearing_angle,radius))
    del ins_cursor

    #projected coordinate system used for output lines feature classes generated
    project_coordsys = """PROJCS['NAD_1927_StatePlane_Alabama_East_FIPS_0101',GEOGCS['GCS_North_American_1927',
    DATUM['D_North_American_1927',SPHEROID['Clarke_1866',6378206.4,294.9786982]],PRIMEM['Greenwich',0.0],
    UNIT['Degree',0.0174532925199433]],PROJECTION['Transverse_Mercator'],PARAMETER['False_Easting',500000.0],
    PARAMETER['False_Northing',0.0],PARAMETER['Central_Meridian',-85.83333333333333],
    PARAMETER['Scale_Factor',0.99996],PARAMETER['Latitude_Of_Origin',30.5],
    UNIT['Foot_US',0.3048006096012192]];-17948200 -43887100 3048,00609601219;
    -100000 10000;-100000 10000;3,28083333333333E-03;0,001;0,001;IsHighPrecision"""

    arcpy.BearingDistanceToLine_management(bearing_table,clip_lines_fc,"Xcoord","Ycoord",
                                           "Distance","Feet","Bearing",spatial_reference=project_coordsys)
    #adding each circle's 8 lines in iteration to the sum output line feature class
    arcpy.Append_management(clip_lines_fc,sum_lines_fc,"NO_TEST")

    #deleting temp feature classes to avoid locking issues at next iteration
    arcpy.Delete_management(bearing_table)
    arcpy.Delete_management(clip_lines_fc)

#cutting each circle in the polygon feature class by using the lines obtained earlier
arcpy.FeatureToPolygon_management([in_poly_fc,sum_lines_fc],"Quadrants","#","ATTRIBUTES","#")

The source polygon feature class with circles:

enter image description here

The processed circles, each divided into 8 segments:

enter image description here

6

Thanks for your script Alex, very nice!

I saw a small issue, since relies on several geoprocessing tools that run for each row: Create Table > Add Field > Cursor to Insert Rows > Bearing Distance To Line > Append > Delete Table > Delete Lines >>> Reiterate for next row. It took 10 secs per feature for me. Since, I needed to process around 18,000 features (e.g. 50 hour runtime), it wasn't very scalable.

I also encountered the problem of FeatureToPolygon_management creating slivers because it planarized all the pie segments such that each overlap became its own small polygon (see below). It also requires an Advanced license to run (scarce in our group).

Planarization causing slivers

I expanded on the script to run on any license level and perform all trig calculations directly in Python so we don't have to rely on the GP overhead.

__author__ = "John K. Tran, Michael Tarrant, Alex Tereshenkov"
__contact__ = "jtran20@masonlive.gmu.edu, http://gis.stackexchange.com/users/14435/alex-tereshenkov"
__version__ = "3.0"
__created__ = "6/30/15"
__credits__ = "http://gis.stackexchange.com/questions/94465/how-to-split-circles-in-8-quadrants"

"""Cuts each circle in a circular input polygon feature class (e.g. from a buffer tool) into
pie segments based on a user-specified number of slices."""

import arcpy
import math
import os

arcpy.env.overwriteOutput = True
arcpy.SetProgressor('default', "Firing up script...")

# Set up initial parameters.
arcpy.SetProgressorLabel("Setting up initial parameters")
fc = arcpy.GetParameterAsText(0) # A polygon feature class consisting of circles (e.g. derived from a buffer).
outfc = arcpy.GetParameterAsText(1) # The output polygon feature class cut into pie pieces.
numslices = arcpy.GetParameter(2) # Defines number of slices to cut each circle.
degrees = [360.0/float(numslices)*i for i in range(0, numslices)]
radians = [deg*(math.pi/180.0) for deg in degrees]
spatialref = arcpy.Describe(fc).spatialReference
finalpies = []

# Calculating pie segments from input. Takes the circle geometry, creates a "cutting line" based on the bearing points and centroid, then cuts the circle geometry, returning the resulting pie segment in the 'finalpies' list.
count1 = 0
with arcpy.da.SearchCursor(fc, "SHAPE@") as searchcursor:
    for row in searchcursor:
        if count1 % 100 == 0:
            arcpy.SetProgressorLabel("Calculating pie segments from input: Currently on row {0}".format(str(count1)))
        geom = row[0]
        centroid = geom.trueCentroid
        circumference = geom.length
        radius = circumference/(2*math.pi) # Since Diameter = 2*pi*Radius >>> Radius = Diameter/(2*pi)
        ##radius *= 1.001 # Add an extra bit to ensure closure.
        bearingpoints = []
        cuttinglines = []
        oldbearingpoint = None # Set up an initial old bearing point value to seed the cutting line.
        for radian in radians:
            xcoord = centroid.X + math.sin(radian)*radius # Given a radius and angle, the remaining legs of a right triangle (e.g. the x and y 
            ycoord = centroid.Y + math.cos(radian)*radius # displacement) can be obtained, where x = sin(theta)*radius and y = cos(theta)*radius.
            bearingpoint = arcpy.Point(xcoord, ycoord) # Bearing point is analogous to a polar coordinate system. It's a location with respect to a distance and angle (measured clockwise from north) to a reference point (e.g. the circle centroid).
            bearingpoints.append(bearingpoint)
            if oldbearingpoint:
                cuttingline = arcpy.Polyline(arcpy.Array([oldbearingpoint, centroid, bearingpoint]), spatialref) # Cutting line is the line created by connecting the previous bearing point, centroid, and current bearing point to make a pie sector.
                cuttinglines.append(cuttingline)
            oldbearingpoint = bearingpoint
        cuttinglines.append(arcpy.Polyline(arcpy.Array([bearingpoints[-1], centroid, bearingpoints[0]]), spatialref))
        for eachcuttingline in cuttinglines:
            pie1, pie2 = geom.cut(eachcuttingline) # Cut the pie using the native arcpy.Geometry() "cut" method.
            if pie1 and pie2: # Since cutting results in two polygon features (left + right), but we don't know which polygon contains the "pie sector" and which polygon contains "the rest of the pie",
                if pie1.area < pie2.area: # we have to compare their areas. The target pie sector (for slice numbers greater than 2) will be smaller than "the rest of the pie".
                    finalpie = pie1 # If pie1 is smaller, use pie1.
                elif pie1.area > pie2.area:
                    finalpie = pie2 # If pie2 is smaller, use pie2.
                else:
                    raise ArithmeticError("I encountered an internal error - both pieces were the same size and I couldn't identify the target piece from the rest of the pie (e.g. if Number of Slices = 2). See John to troubleshoot.")
            else:
                raise ValueError("I encountered an internal error - the cutting line didn't cut the pie, so one piece evaluated to 'None'. See John to troubleshoot.")
            finalpies.append(finalpie)
        count1 += 1
del searchcursor

# Create a blank polygon feature class and insert each pie sector.
count2 = 1
arcpy.CreateFeatureclass_management(os.path.dirname(outfc), os.path.basename(outfc), "POLYGON", None, "DISABLED", "DISABLED", spatialref)
with arcpy.da.InsertCursor(outfc, "SHAPE@") as insertcursor:
    for eachpie in finalpies:
        if count2 % 100 == 0:
            arcpy.SetProgressorLabel("Writing pie segments to output: Currently on row {0}".format(str(count2)))
        row = (eachpie,)
        insertcursor.insertRow(row)
        count2 += 1
del insertcursor

arcpy.ResetProgressor()

It just requires a script tool that accepts as parameters:

  1. An input polygon feature class (Type: Feature Class,)
  2. An output polygon feature class (Type: Feature Class; Direction: Output)
  3. The number of slices to cut the pie (Type: Long)

Or you can run it directly with a few modifications by hard-coding the GetParameterAsText variables and replacing SetProgressorLabel with standard prints.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.