11

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 ArcPy or ModelBuilder) [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.

How can I accomplish this task?


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

0

6 Answers 6

17

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é.

5
  • 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! Commented Jul 15, 2013 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
    Commented Jul 15, 2013 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
    Commented Jul 15, 2013 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
    Commented Jul 15, 2013 at 17:34
  • Thanks so much for your answer PolyGeo! I was actually pretty impressed that the code worked without modifications. I'm running ArcGIS Desktop 10.6
    – Rie Mino
    Commented Mar 2, 2019 at 2:51
10

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!

4
  • is it possible to shift feature by the value inside table, stored in column?
    – Losbaltica
    Commented Feb 1, 2017 at 7:52
  • 1
    It should be. Just assign the ShiftX and ShiftY parameters to the appropriate columns. Commented Feb 1, 2017 at 18:25
  • I'm confused by what you are passing in here as "shape". Can you help me out please? Commented May 25, 2017 at 20:18
  • The "Expression" shows the parameters that are being passed into the code-block function called shiftXYCoordinates(). So the first parameter is !SHAPE!, which is the shape field from the layer. Commented May 26, 2017 at 15:42
5

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.

1
  • 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
    Commented Jul 15, 2013 at 15:58
5

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)

4

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
2

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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