Arcpy (2.6) modify multiparts polygon vertice xy vlaue

1238
17
Jump to solution
08-30-2016 02:14 AM
tinyMee
New Contributor

Dear all,

I am using python2.6 (together with arcgis 10.0) and try to modify the vertice xy values for multiple parts polygon using the following scripts, no error thrown but the vertice xy value not changed. Please help to advice what is wrong in my script:

def modify(f, n):
     s = '%.12f' % f
     i, p, d = s.partition('.')
     return '.'.join([i, (d+'0'*n)[:n]])

import arcpy
import logging
from arcpy import env

env.workspace = r"C:\modifyTest"
infc = "testing.mdb/donutTesting1"
desc = arcpy.Describe(infc)
shapefieldname = desc.ShapeFieldName
my_field = "SHAPE"
rows = arcpy.UpdateCursor(infc) 
for row in rows:
     feat = row.getValue(shapefieldname) 
     arr_pol = arcpy.Array()
     partnum = 0
     if feat.isMultipart == True: 
          for part in feat:
               parts = []
               arr_parts = arcpy.Array()
 
               while partnum < feat.partCount:
                    part = feat.getPart(partnum)
                    pnt = part.next()
                    while pnt: 
                         X_value = modify( pnt.X , 2)
                         Y_value = modify( pnt.Y , 2)
                         pnt.X= X_value
                         pnt.Y= Y_value
                         #print pnt.X, pnt.Y
                         parts.append([X_value, Y_value])
                         XYPoint = arcpy.Point(X_value, Y_value)
                         arr_parts.add(XYPoint) 
                         arr_pol.add(arr_parts)
 
                         pnt = part.next()
                         if pnt is None:
                              pnt = part.next()
                              if pnt:
                                   X_value = modify( pnt.X , 2)
                                   Y_value = modify( pnt.Y , 2)
                                   pnt.X= X_value
                                   pnt.Y= Y_value
                                   parts.append([X_value, Y_value])
                                   XYPoint = arcpy.Point(X_value, Y_value)
                                   arr_parts.add(XYPoint) 
                                   arr_pol.add(arr_parts) 
                    partnum += 1
              print parts
      polygon = arcpy.Polygon(arr_pol)
      row.setValue(my_field,polygon)
      rows.updateRow(row)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
Tags (2)
0 Kudos
1 Solution

Accepted Solutions
XanderBakker
Esri Esteemed Contributor

Apart from the indentation there are some funcional errors in your script. You only process the multipart polygons, while the final polygon is created from a arr_pol, which will be empty when the feature is not a multipart. You should process all features the same way regardless of it being multi or single part (all  the features in your sample data have only 1 part). If you don't do that and you write the original polygons when it is single part, you will get this:

And yes rounding the coordinates on 2 decimales is visible in you data. I didn't your code, but the da cursor (ArcGIS 10.1 and higher) which is far more efficient. 

Code used (to produce the "problem" above):

def main():
    import arcpy

    fc = r'D:\Xander\GeoNet\Donuts\shp\donutTesting.shp'
    fc_out = r'D:\Xander\GeoNet\Donuts\shp\donutTesting_out.shp'
    sr = arcpy.Describe(fc).spatialReference

    lst_feats = []
    with arcpy.da.UpdateCursor(fc, ('SHAPE@', 'OID@')) as curs:
        for row in curs:
            polygon = row[0]
            oid = row[1]
            print oid
            if polygon.isMultipart:
                print "multipart, # of parts:", polygon.partCount
                lst_pol = []
                for i in range(polygon.partCount):
                    part = polygon.getPart(i)
                    print" - part", i
                    lst_part = []
                    for pnt in part:
                        pnt2 = roundCoords(pnt, 2)
                        print "   - pnt", pnt, pnt2
                        lst_part.append(pnt2)
                    lst_pol.append(lst_part)
                polygon2 = arcpy.Polygon(arcpy.Array(lst_pol), sr)
                lst_feats.append(polygon2)
            else:
                print "singlepart, # of parts:", polygon.partCount
                lst_feats.append(polygon)

    arcpy.CopyFeatures_management(lst_feats, fc_out)


def roundCoords(pnt, decimals):
    if pnt:
        return arcpy.Point(round(pnt.X, decimals), round(pnt.Y, decimals))
    else:
        return pnt

if __name__ == '__main__':
    main()

Output messages:

0
singlepart, # of parts: 1
1
multipart, # of parts: 1
 - part 0
   - pnt 48371,4859999996 42834,2699999996 NaN NaN 48371,49 42834,27 NaN NaN
   - pnt 48372,1289999997 42966,5089999996 NaN NaN 48372,13 42966,51 NaN NaN
   - pnt 48371,9040000001 42977,0130000003 NaN NaN 48371,9 42977,01 NaN NaN
   - pnt 48400,7130000005 42992,7489999998 NaN NaN 48400,71 42992,75 NaN NaN
   - pnt 48620,4069999997 42952,8029999994 NaN NaN 48620,41 42952,8 NaN NaN
   - pnt 48627,1799999997 42891,8430000003 NaN NaN 48627,18 42891,84 NaN NaN
   - pnt 48481,5530000003 42841,0429999996 NaN NaN 48481,55 42841,04 NaN NaN
   - pnt 48371,4859999996 42834,2699999996 NaN NaN 48371,49 42834,27 NaN NaN
   - pnt None None
   - pnt 48435,8629999999 42969,7170000002 NaN NaN 48435,86 42969,72 NaN NaN
   - pnt 48466,034 42961,6860000007 NaN NaN 48466,03 42961,69 NaN NaN
   - pnt 48468,5360000003 42963,7890000008 NaN NaN 48468,54 42963,79 NaN NaN
   - pnt 48470,3729999997 42965,7080000006 NaN NaN 48470,37 42965,71 NaN NaN
   - pnt 48435,8629999999 42969,7170000002 NaN NaN 48435,86 42969,72 NaN NaN
   - pnt None None
   - pnt 48391,9639999997 42944,5050000008 NaN NaN 48391,96 42944,51 NaN NaN
   - pnt 48408,0039999997 42942,8200000003 NaN NaN 48408 42942,82 NaN NaN
   - pnt 48416,9890000001 42950,9759999998 NaN NaN 48416,99 42950,98 NaN NaN
   - pnt 48391,4349999996 42957,2050000001 NaN NaN 48391,43 42957,21 NaN NaN
   - pnt 48391,9639999997 42944,5050000008 NaN NaN 48391,96 42944,51 NaN NaN
2
singlepart, # of parts: 1

Doing this (the right way) in ArcGIS 10.0, you could use this code:

def main():
    import arcpy

    fc = r'D:\Xander\GeoNet\Donuts\shp\donutTesting.shp'
    fc_out = r'D:\Xander\GeoNet\Donuts\shp\donutTesting_out2.shp'
    sr = arcpy.Describe(fc).spatialReference
    fld_shp = arcpy.Describe(fc).ShapeFieldName
    fld_oid = arcpy.Describe(fc).OIDFieldName

    curs = arcpy.UpdateCursor(fc)
    for row in curs:
        polygon = row.getValue(fld_shp)
        oid = row.getValue(fld_oid)
        print "oid", oid
        lst_pol = []
        for i in range(polygon.partCount):
            part = polygon.getPart(i)
            print" - part", i
            lst_part = []
            for pnt in part:
                pnt2 = roundCoords(pnt, 2)
                print "   - pnt", pnt, pnt2
                lst_part.append(pnt2)
            lst_pol.append(lst_part)
        polygon2 = arcpy.Polygon(arcpy.Array(lst_pol), sr)
        row.setValue(fld_shp, polygon2)
        curs.updateRow(row)
    del curs, row

def roundCoords(pnt, decimals):
    if pnt:
        return arcpy.Point(round(pnt.X, decimals), round(pnt.Y, decimals))
    else:
        return pnt

if __name__ == '__main__':
    main()

Zoom in to coordinates 48391.435  42957.205 Meters to see the effect. 

View solution in original post

17 Replies
DanPatterson_Retired
MVP Esteemed Contributor

please format your code using Syntax Highlighting inside Advanced Editor, More...

/blogs/dan_patterson/2016/08/14/script-formatting 

in newer version of arcpy FeatureClass properties—Help | ArcGIS for Desktop  and shapefieldName

but "Shape" is safest

 raw formatting for paths r"c:\yourpath"

You aren't going to see anything in the table.  did you add the file into a new data frame? refresh the view?

tinyMee
New Contributor

Thank you Dan, already format the code. I didn't add it in new data frame or refresh the view. The script works for single part polygon.

0 Kudos
DanPatterson_Retired
MVP Esteemed Contributor

are the points printing out on line 33?

line 44 has a 1 space extra indent you should fix

line 52, what does it print out if you indent it one more level?

So it works for a single part but not a multipart?  If that is the case, I would throw some more print statements.  If you could, select one of your multipart features and either save it out to a shapefile, zip and post and I could dissect with some numpy tools I use.  I presume you want some sort of modification but I am not sure what you want since you are using old school formatting and I am not clear whether you are returning a string or a number since there is no modification to the string (from "".join...) into float

tinyMee
New Contributor

Hi Dan,

Yes the points printing out in line 33

line 52, if you indent it one more level the print out result is the same

The shapefile is attached for reference.

Thank you so much....

0 Kudos
DanPatterson_Retired
MVP Esteemed Contributor

before I did into this... what is the purpose of the modify function?  I see you are partitioning the string, but it returns a string and I am not seeing how this is related to modifying the geometry.  Can you provide an example of the input and the output you are looking for

0 Kudos
tinyMee
New Contributor

Hi Dan, the purpose is to modify the xy value of the vertex. For example, one vertex coordinate is (123.456, 213.672), I want to change it to (123.45, 213.67).

0 Kudos
XanderBakker
Esri Esteemed Contributor

I'm intrigued.. why do you want to cut of the third decimal (apart from not rounding it)? 

0 Kudos
DanPatterson_Retired
MVP Esteemed Contributor

well the approach that you have propose yields a string.  Should you need to round the coordinates, then you definitely need to round them in numeric form and not in string form

>>> x = 123.456
>>> round(x,2)
123.46‍‍‍

So you should correct your title to reflect this and get rid of the extraneous 'def'

pnt.X = round(pnt.X, 2)
tinyMee
New Contributor

Hi Dan, thanks for the advice. You are right that I should round the xy value.

The most important thing is I cannot change the vertex xy value for multiple parts polygon at all, don't know whether there is a way to change the vertex value for multiple parts polygon.

Thanks again. 

0 Kudos