4

I am writing a script that can take a .txt file as input and as output has a shapefile containing polygons to be used in ArcMap. My input looks like this with points at each vertex of a polygon, and the opening and closing point at the same location:

0,1013757671.0,1,557286.0000000,6349610.0000000 ...

With the order being: FID, ID, POINT, X, Y this has slightly changed to FID, ID, X, Y, ID as integer starting at '1'

I am able to make a list containing this information as separate elements like below and with their respective type (like integer, float etc):

[[0, 1013757671.0, 1, 557286.0, 6349610.0, 1], ...

Then I create a new shapefile that is to contain the polygons with an existing spatial reference taken from another file:

sr = arcpy.Describe(r"C:\Users\B026349\Documents\data\export\Site_101231911.shp").spatialReference

arcpy.CreateFeatureclass_management(outPath, outFile, "POLYGON", "", "DISABLED", "DISABLED", sr)...

However, I am stuck with writing the polygons to this new shapefile, since one polygon needs to be created per ID (the second number in each row of the list of elements).

I tried the following code based on the documentation on writing geometries (link), but I get a shapefile in which all columns are filled with a 0 and there are no polygons:

cur = arcpy.da.InsertCursor(outFile, ["SHAPE@"])

array = arcpy.Array()

ID = 1013757671.0 #starting ID
for row in splitData: #list containing the data
    if ID == 1013757671.0:
        ID = row[1]  #ID field, when this changes it should create a new polygon
        if ID != row[1]:
            cur.insertRow([arcpy.Polygon(array)])
            array.removeAll()
        array.add(arcpy.Point(row[3], row[4], ID=row[1]))
        ID = row[1]
    cur.insertRow([arcpy.Polygon(array)])

if cur:
   del cur

How can I make it function?


Based on the comments I have made some changes to the code above but my results are the following:

enter image description here

I mapped the points and color coded them to indicate which should be connected to form a polygon, but as can be seen this doesn't happen very well.


I tried both methods posted as answers. The first changed my code to the following to match my data stream (which has slightly changed into: [[0, 1013757671.0, 557286.0, 6349610.0, 1] or respectively, FID, ID, X, Y and ID as a sequence of integers)

file = "C:\Users\B026349\Documents\scripts\Points.csv"

sr = arcpy.Describe(r"C:\Users\B026349\Documents\data\export\Site_101231911.shp").spatialReference #ETRS_1989_UTM_Zone_32N

arcpy.CreateFeatureclass_management(outPath, outFile, "POLYGON", "", "DISABLED", "DISABLED", sr)

ddict = defaultdict(list)
with open(file) as reader:
    for fi,i,x,y,l in csv.reader(reader): #'l' are the ID's (i) reduced to a numerical sequence, for example: 1, 1, 1, 2, 2, 3, 3, 3 
        ddict[int(l)].append(arcpy.Point(X=float(x),Y=float(y)))

with arcpy.da.InsertCursor(outFile, "SHAPE@") as iCursor:
    for key,verts in ddict.items():
        iCursor.insertRow([arcpy.Polygon(arcpy.Array(verts))])

My results have improved, but there are still issues and they look like the following now:

enter image description here

With the second suggested method my code looks like this:

sr = arcpy.Describe(r"C:\Users\B026349\Documents\data\export\Site_101231911.shp").spatialReference #ETRS_1989_UTM_Zone_32N

arcpy.CreateFeatureclass_management(outPath, outFile, "POLYGON", "", "DISABLED", "DISABLED", sr)
id = splitData[0][4]                    
array = arcpy.Array()                   

with arcpy.da.InsertCursor(outFile, ['ID', 'SHAPE@']) as ic:
    for k, row in enumerate(splitData):   
        if row[4] == id:                  
            array.append(arcpy.Point(row[2], row[3]))
        else:                            
            poly = arcpy.Polygon(array)   
            ic.insertRow([id, poly])      
            array.removeAll()             
            id = row[4]                  
            array.append(arcpy.Point(row[2], row[3]))   
        if k == len(splitData):           
            poly = arcpy.Polygon(array)
            ic.insertRow([id, poly])

This code gives me this result:

enter image description here

I guess im getting closer but there is still something wrong.


I fixed the problem, the issue was that the coordinates were calculated without added precision behind the dot. Therefore, some coordinates where on the same location giving weird polygon results. The code block that uses a 'dictionary approach' with input as a csv gives the expected result. The other suggested method does not attach the last polygon and im not sure why.

My datastream:

[0, 557285.65, 6349611.13, 1] equal to FID (int), X(float), Y(float), POLYGON ID(int)

enter image description here

  • Did you have any ID is equal -1 in your text file. – Shiuli Pervin Nov 17 '16 at 10:40
  • No it starts at 1013757671.0 as it is a subset of a larger dataset. I thought this was to start a counter. – Tins Nov 17 '16 at 11:07
  • Changing ID = -1 to the first ID in my own data does not change the output. – Tins Nov 17 '16 at 11:21
  • Your for loop says "for rows" then the rest of your code reference "row" – Hornbydd Nov 17 '16 at 14:33
  • I changed it and edited my main post, but the results aren't very great still. – Tins Nov 17 '16 at 14:51
3

Instead of grouping the points during the for loop, do it before hand (shown here while reading the csv). The collections module works really well here:

import csv
from collections import defaultdict
import arcpy

data = 'my csv'
sr = arcpy.SpatialReference(3857)

temp = arcpy.CreateFeatureclass_management('in_memory', 'poly',
                                           "POLYGON", "", "DISABLED", "DISABLED", sr)[0]

ddict = defaultdict(list)
# Read csv into default dict
# the keys are the ID and the values are a list of lists of arcpy.Point() objects
with open(data) as reader:
    for i,x,y in csv.reader(reader):
        ddict[int(i)].append(arcpy.Point(X=float(x),Y=float(y)))

with arcpy.da.InsertCursor(temp, "SHAPE@") as iCursor:
    for key,verts in ddict.items():
        iCursor.insertRow([arcpy.Polygon(arcpy.Array(verts))])

Here's the sample csv. Extending it to support your data stream (with ID and only particular FIDs) should be straightforward.

1,-17320492.3694,2273548.0937
1,-17300448.2977,2293498.6846
1,-17280839.9667,2277722.0269
1,-17288683.2991,2262422.167
1,-17320492.3694,2273548.0937
2,-17312213.2963,2205042.6087
2,-17281711.4481,2243893.6161
2,-17269946.4495,2201808.5922
2,-17312213.2963,2205042.6087
3,-17327028.4797,2239727.2029
3,-17339229.219,2202732.5413
3,-17360580.5128,2222608.2395
3,-17344893.848,2258251.6495
3,-17367116.6231,2275866.83
3,-17332257.368,2286536.7405
3,-17327028.4797,2239727.2029
  • I have tried your suggestion but my results are still not great, I edited my main post with the new code and an image of the results it generated. – Tins Nov 18 '16 at 12:57
  • 1
    @Tinsley, without knowing what your data is supposed to look like or how your csv is formed (ie, are the points ordered correctly), it's very difficult to say what the problem is. – Paul Nov 18 '16 at 17:39
1

`

cur = arcpy.da.InsertCursor(outFile, ["SHAPE@"])

array = arcpy.Array()

ID = 1013757671.0 #starting ID
for row in splitData:
    if ID == 1013757671.0:    # <- and what if it's not?
        ID = row[1]  
        if ID != row[1]:      # <- it's True in any case because of previous line
            cur.insertRow([arcpy.Polygon(array)])
            array.removeAll()
        array.add(arcpy.Point(row[3], row[4], ID=row[1]))  # <- You don't have to put ID in geometry field
        ID = row[1]
    cur.insertRow([arcpy.Polygon(array)])

if cur:
   del cur

`

So, it should be something like:

id = splitData[0][1]                      # write id from data, not manually
array = arcpy.Array()                     # blanc array to store geometry
#you don't have to 'del cur' if you use 'with'
with arcpy.da.InsertCursor(outFile, ['ID', 'SHAPE@']) as ic:
    for k, row in enumerate(splitData):   # to store sequence number
        if row[1] == id:                  # if id_now == i_prev -> add next point
            array.append(arcpy.Point(row[3], row[4]))
        else:                             # if it's a new id in a row
            poly = arcpy.Polygon(array)   # create arcpy.polygon
            ic.insertRow([id, poly])      # insert new row with [ID, GEOMETRY]
            array.removeAll()             # empty the array for the new points
            id = row[1]                   # get next ID
            array.append(arcpy.Point(row[3], row[4]))   # write first point to it
        if k == len(splitData):           # write the last row in splitData
            poly = arcpy.Polygon(array)
            ic.insertRow([id, poly])

Before the block add new field 'ID' as long to write your polygon's ids there.

  • Hi, thank you for your reply. I also tried your suggestion but it is still not functioning very well. I edited my main post with the code and my results as seen in ArcMap. – Tins Nov 18 '16 at 12:58

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.