Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

I have a number non-georeferenced CAD layers (see this question) that have text annotation features. I have created a model to convert the text to points, but after converting the annotation to a Point featureclass, I see that the CAD text anchor points do not coincide with the center of the CAD text (which is where the points belong).

Therefore, I would like to programatically (using ModelBuilder or ArcPy) [move] a feature relative to its current location (delta x,y) using a measured X,Y value that I will provide.

This would allow me to move the GIS points back to where they belong, instead of the offset CAD anchor point. Any ideas on how I can accomplish this task?

UPDATE: @PolyGeo gave an excellent answer using SHAPE@XY IN 10.1, but currently I am running 10.0. Any 10.0 ideas?

share|improve this question

6 Answers 6

This code should do it using the SHAPE@XY token that came with arcpy.da.UpdateCursor in ArcGIS 10.1.

import arcpy
# Set some variables
fc = r"C:\temp\test.gdb\testFC"
fc2 = r"C:\temp\test.gdb\testFCcopy"
xOffset = 0.001
yOffset = 0.001
# Code to make a copy which will have its coordinates moved (and can be compared with original)
if arcpy.Exists(fc2):
    arcpy.Delete_management(fc2)
arcpy.Copy_management(fc,fc2)
# Perform the move
with arcpy.da.UpdateCursor(fc2, ["SHAPE@XY"]) as cursor:
    for row in cursor:
        cursor.updateRow([[row[0][0] + xOffset,row[0][1] + yOffset]])

The coding pattern used here came from ArcPy Café.

share|improve this answer
    
Ugh! It took me until this morning to realize that SHAPE@XY is only available in 10.1, and my company is still using 10.0. This is a great answer (going forward), but I'm going to wait and see if anyone has any suggestions for 10.0. Thanks! –  RyanDalton Jul 15 '13 at 14:20
    
More information on a similar process for anybody reading this. Still 10.1. arcpy.wordpress.com/2013/06/07/disperse-overlapping-points –  theJones Jul 15 '13 at 15:03
    
Is this actually setting the values anywhere? Never used the UpdateCursor like that before. Usually I do += and then update the row. Otherwise only thing I do different in my version is the UpdateCursor uses ['SHAPE@X', 'SHAPE@Y'] so you can access them as row[0] and row[1] instead of having to do the row[0][0] and row[0][1]. Think its just a bit easier to read for me. –  eseglem Jul 15 '13 at 16:03
    
Yes, that is a valid way to update the rows. Actually, I had never seen a value passed in updateRow() until a few weeks ago. It was actually an example for updating the geometry. –  Paul Jul 15 '13 at 17:34
1  
@polygeo isn't it row[0][1] for the Y ? –  radouxju Apr 7 at 20:03

You may also use this field calculator script to move feature locations:

def XYsetVALUE( shape, X_value, Y_value): 
  myMoveX = 0.001
  myMoveY = 0.001
  point = shape.getPart(0) 
  point.X = X_value + myMoveX
  point.Y = Y_value + myMoveY
  return point 

XYsetVALUE ( !SHAPE!, !X_COORD!, !Y_COORD! )

You could include an extra Calculate Field method within your model using the function above.

share|improve this answer
    
Thats an interesting way to do it, I didn't actually know you could field calculate on the shape field. This may actually be the easiest way to get it done if it is a set offset for all points. It would probably be faster to do point.X += myMoveX and point.Y += myMoveY instead of needing to pass in X and Y coordinates for it though. –  eseglem Jul 15 '13 at 15:58

As you can see, it's a lot easier in 10.1 when you get access to cursor tokens.

import arcpy
# Code to move features in copy of same dataset
fc = r"C:\temp\test.gdb\testFC"
fc2 = r"C:\temp\test.gdb\testFCcopy"
xOffset = 0.001
yOffset = 0.001
if arcpy.Exists(fc2):
    arcpy.Delete_management(fc2)
arcpy.Copy_management(fc, fc2)

shape = arcpy.Describe(fc2).ShapeFieldName

cursor = arcpy.UpdateCursor(fc2)
for row in cursor:    
    point = row.getValue(shape).getPart()
    row.setValue(shape, arcpy.Point(point.X + xOffset, point.Y + yOffset))
    cursor.updateRow(row) 

del point, row, cursor
share|improve this answer
up vote 3 down vote accepted

I credit @artwork21 for leading me to my final solution. I actually found a nearly complete script in the ArcGIS 10.0 online help article called "Calculate Field examples", listed under the subcategory "Code samples—geometry" and "For a point feature class, shift the x coordinate of each point by 100"

The final script that I used within the ModelBuilder "Calculate Field" tool was:

Expression: shiftXYCoordinates(!SHAPE!,%ShiftX%,%ShiftY%)

where ShiftX and ShiftY are variables (as parameters) defined on the ModelBuilder canvas.

Expression Type: PYTHON_9.3

Code Block:

def shiftXYCoordinates(shape,x_shift,y_shift):
   point = shape.getPart(0)
   point.X += float(x_shift)
   point.Y += float(y_shift)
   return point

Since all models work on a selected set, you should also be able to create this as a generic tool that will work in conjunction with other models/tools in other modelbuilder sessions. The very simple model I created (as a "plugin" to other models to shift coordinate values) looks like this. That way I can control the shift on a per-selection-set basis (as defined in other models):

ShiftXY Model

It worked like a charm, thank you all for your input!

share|improve this answer

I adapted the solution to move / shift points point into a certain direction (angle) and a given distance.

Looks like:

def shiftXYCoordinates(shape,angle,distance):
point = shape.getPart(0)
point.Y += distance * math.cos(math.radians(angle))
point.X += distance * math.sin(math.radians(angle))
return point

and be called like shiftXYCoordinates(!SHAPE!, !Angle! ,5000), if you have a field “angle” for your points features (or with a constant of course). Angle should be given in decimal degrees. 0 will shift “up”, 90 “right” etc. I got them after creating strip map index features and converting those to points.

Also make sure to select Field Name “Shape” before running :)

(Solution tested in ArcMap 10.0 SP5)

share|improve this answer

This works for 10.0:

# Featureclass here
FC = r'featureclass'

fcount = 0
shapefield = arcpy.Describe(FC).shapeFieldName
featureUpdate = arcpy.UpdateCursor(FC)
for f in featureUpdate:
    # Hard coded shifts but easy enough to set up a lookup function if needed
    sLon = 0.001
    sLat = 0.001
    # Optional but I like to count to see where it is at in the process
    if fcount % 1000 == 0:
        print('Updating feature %s...' %(fcount))
    # Get the original value
    cF = f.getValue(shapefield)
    cPNT = cF.getPart()
    # Create a new point with the shifted value
    sPNT = arcpy.Point(cPNT.X - sLon, cPNT.Y - sLAT)
    # Set the shapefield to the new point and update feature
    f.setValue(shapefield, sPNT)
    featureUpdate.updateRow(f)
    fcount += 1
del featureUpdate

Edit: Added some comments for better explanation, and a typo.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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