Select to view content in your preferred language

Migrate tool/script from ArcMap to ArcGIS Pro

2482
20
Jump to solution
03-11-2024 10:59 AM
TonyAlmeida
Frequent Contributor

I have a tool/script that performed exceptionally well in ArcMap both as a tool/script and as an Add-in. However, I've been attempting to convert it to ArcGIS Pro without success. This tool/script in ArcMap allowed users to calculate the distance between two points with just two clicks, essentially creating a polyline that conveyed the length for further calculations. It also generated a point and updated a field based on this calculation. Although I've attempted to replicate the parameters from ArcMap in Pro, the tool fails to function as expected. It doesn't allow me to click on the map or initiate any actions. How can I go about ensuring this script works in ArcGIS Pro?

The noticeable difference that I see in the parameters section is that script in pro doesn't have Schema.

ArcMap parameters:

Input - Feature Set-

Type - Required

Direction - Input

MultiValue - no

Schema - points to the Point layer

 

Script,

 

import arcpy
from arcpy import env
import pythonaddins
import os

fc = "Points"
arcpy.env.workspace = r"C:\Temp\PointsTest.mdb"
pointLayer = arcpy.env.workspace + os.sep + "Points" #target point feature class 

if arcpy.Exists ("line_test"): 
    arcpy.Delete_management ("line_test")

# Start an edit session. Must provide the worksapce.
edit = arcpy.da.Editor(arcpy.env.workspace)

# Edit session is started without an undo/redo stack for versioned data
#  (for second argument, use False for unversioned data)
edit.startEditing(True)

# Start an edit operation
edit.startOperation()

input = arcpy.GetParameterAsText(0)
Range = float(arcpy.GetParameterAsText(1))
# Create empty Point and Array objects
point = arcpy.Point()
array = arcpy.Array()

# A list that will hold each the Polyline object
featureList = []

rows = arcpy.SearchCursor(input)
for row in rows:
    geom = row.Shape
    point.X = geom.centroid.X
    point.Y = geom.centroid.Y
    # Add each point to the array
    array.add(point)    

# Create the polyline
polyline = arcpy.Polyline(array)
# Clear the array for future use
array.removeAll()
featureList.append(polyline)

del row, rows

# Copy the polyline to file geodatabase to create a Shape_Length field
arcpy.CopyFeatures_management(featureList, "line_test")
with arcpy.da.SearchCursor("line_test", ["SHAPE@","SHAPE@LENGTH"]) as cursor:
    for row in cursor:
        X = row[0].lastPoint.X
        Y = row[0].lastPoint.Y
        length = row[1] / 5.28
        print("Length:", length)

row_value = (length, (X, Y))
        
cursor = arcpy.da.InsertCursor(pointLayer, ("SiteNum", "SHAPE@XY"))
cursor.insertRow(row_value)

 

 

Add-in,

import arcpy
import pythonaddins

class PointTool(object):
    """Implementation for Point_addin.PointTool (Tool)"""
    def __init__(self):
        self.enabled = True
        self.cursor=3
        self.shape = "Line" # Can set to "Line", "Circle" or "Rectangle" for interactive shape drawing and to activate the onLine/Polygon/Circle event sinks.
        def onLine(self, line_geometry):
            
        fc = "Points"
        arcpy.env.workspace = r"C:\Temp\PointsTest.mdb"
        pointLayer = arcpy.env.workspace + os.sep + "Points" #target point feature class 

        if arcpy.Exists ("line_test"): 
            arcpy.Delete_management ("line_test")

        # Start an edit session. Must provide the worksapce.
        edit = arcpy.da.Editor(arcpy.env.workspace)

        # Edit session is started without an undo/redo stack for versioned data
        #  (for second argument, use False for unversioned data)
        edit.startEditing(True)

        # Start an edit operation
        edit.startOperation()

        input = arcpy.GetParameterAsText(0)
        Range = float(arcpy.GetParameterAsText(1))
        # Create empty Point and Array objects
        point = arcpy.Point()
        array = arcpy.Array()

        # A list that will hold each the Polyline object
        featureList = []

        rows = arcpy.SearchCursor(input)
        for row in rows:
            geom = row.Shape
            point.X = geom.centroid.X
            point.Y = geom.centroid.Y
            # Add each point to the array
            array.add(point)    

        # Create the polyline
        polyline = arcpy.Polyline(array)
        # Clear the array for future use
        array.removeAll()
        featureList.append(polyline)

        del row, rows

        # Copy the polyline to file geodatabase to create a Shape_Length field
        arcpy.CopyFeatures_management(featureList, "line_test")
        with arcpy.da.SearchCursor("line_test", ["SHAPE@","SHAPE@LENGTH"]) as cursor:
            for row in cursor:
                X = row[0].lastPoint.X
                Y = row[0].lastPoint.Y
                length = row[1] / 5.28
                print("Length:", length)

        row_value = (length, (X, Y))
                
        cursor = arcpy.da.InsertCursor(pointLayer, ("SiteNum", "SHAPE@XY"))
        cursor.insertRow(row_value)

 

 

 

1 Solution

Accepted Solutions
JakeSkinner
Esri Esteemed Contributor

Try the attached tool.  First, update the gdb variable by right-clicking on the toolbox > Properties > Execution:

JakeSkinner_0-1710442949693.png

 

Next, double-click the Script tool.  Click the dropdown next the pencil and select Points.  Create two points and execute the tool.

View solution in original post

0 Kudos
20 Replies
MErikReedAugusta
Frequent Contributor

Full disclosure, I'm not sure any of the below points are the issue, except maybe #2 (Line 49).  That said, a few things that jump out at me in no particular order:

  1. Script, Line 32
    1. It's generally best practice to use the cursors via a with statement to ensure they get closed properly.
    2. The DA cursors are generally more efficient than the standard cursors.  Why use a standard one here?
    3. Compare this to line 50, where you avoid both of these issues.
  2. Script, Line 49
    1. I could be dead-wrong here, but are you sure you want CopyFeatures?  Shouldn't it be CreateFeatureclass?
    2. Reviewing the specs for CopyFeatures, it looks like it requires a Feature Class as input.  I admit I haven't used it much from Python; can it take a list of Polylines?
  3. Script, Line 50
    1. Why call both SHAPE@ and SHAPE@LENGTH?  You can just call one of them.  length is an attribute of that geometry object, just like lastPoint is; see line 5, below:
    2. Not important, but Python 3 has added a new way of inserting variables into strings that can be a bit more user-friendly and with better control.  Personally, I've found it generally more pleasant to use than the older string.format(), %s, and print command concatenation methods.  See line 6, below:

 

with arcpy.da.SearchCursor('line_test', 'SHAPE@') as cursor:
  for row in cursor:
    x = row[0].lastPoint.X
    y = row[0].lastPoint.Y
    length = row[0].length / 5.28
    print(f'Length: {length}')​

 

  • Script, Line 59
    1. See my comments about Line 32, above.
  • Add-in, Lines 11-66
    1. I'm assuming this is a copy-paste issue, but you seem to have an indentation error.
TonyAlmeida
Frequent Contributor

My bad, I had different variations going on.

0 Kudos
JakeSkinner
Esri Esteemed Contributor

Hi @TonyAlmeida,

You could use something like below to create the line feature class:

import os
import arcpy

# Variables
input = arcpy.GetParameterAsText(0)
gdb = r"C:\temp\python\test.gdb"
wkid = 2272

# Environment Variables
arcpy.env.overwriteOutput = True
arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(2272)

arcpy.env.workspace = gdb

array = arcpy.Array()

with arcpy.da.SearchCursor(input, ["SHAPE@X", "SHAPE@Y"]) as cursor:
  for row in cursor:
    pt = arcpy.Point(row[0], row[1])
    array.add(pt)
del cursor

polyline = arcpy.Polyline(array)

arcpy.CopyFeatures_management(polyline, "line_test")
TonyAlmeida
Frequent Contributor

Is there a tool in Pro that I need to use to initiate me to allow me to click on the map?  It currently doesn't allow me to do that with the following parameters. 

Currently using legacy toolbox script.

I have set up the parameters, like so.

Input:

Label - Input

Name - Input

Data type - Feature Set

Type - Required

Direction - Input

Default - points to .lyr file

 

Rang:

Label - Input

Name - Input

Data Type - Double

Type - Required

Direction - Input

 

Current code,

 

import arcpy
import os

# Set up your environment
fc = "Points"
arcpy.env.workspace = r"C:\Temp\PointsTest.gdb"
pointLayer = os.path.join(arcpy.env.workspace, "Points")  # target point feature class

# Check if "line_test" exists and delete it if it does
if arcpy.Exists("line_test"): 
    arcpy.Delete_management("line_test")

# Start an edit session. Must provide the workspace.
edit = arcpy.da.Editor(arcpy.env.workspace)

# Edit session is started without an undo/redo stack for versioned data
# (for the second argument, use False for unversioned data)
edit.startEditing(True)

# Variables
input = arcpy.GetParameterAsText(0)
gdb = r"C:\Temp\PointsTest.gdb"
wkid = 2272

# Set environment variables
arcpy.env.overwriteOutput = True
arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(2272)
arcpy.env.workspace = gdb

array = arcpy.Array()

# Iterate over the features in the input and create a polyline
with arcpy.da.SearchCursor(input, ["SHAPE@X", "SHAPE@Y"]) as cursor:
    for row in cursor:
        pt = arcpy.Point(row[0], row[1])
        array.add(pt)

# Create a polyline from the array
polyline = arcpy.Polyline(array)

# Save the polyline to "line_test" feature class
arcpy.CopyFeatures_management(polyline, "line_test")

# Retrieve the last point of the polyline and calculate its attributes
with arcpy.da.SearchCursor("line_test", "SHAPE@") as cursor:
    for row in cursor:
        X = row[0].lastPoint.X
        Y = row[0].lastPoint.Y
        length = row[0].length / 5.28
        print("Length:", length)

# Prepare the row value tuple
row_value = (length, (X, Y))

# Use arcpy.da.InsertCursor with a context manager to insert rows into the point layer
with arcpy.da.InsertCursor(fc, ["SiteNum", "SHAPE@XY"]) as cursor:
    cursor.insertRow(row_value)

 

 

0 Kudos
JakeSkinner
Esri Esteemed Contributor

Click pencil next to the folder to select the geometry type, and then you should be able to create features

 

 

 

0 Kudos
TonyAlmeida
Frequent Contributor

In the input parameters  dependency I had it pointing to a .lyr file and gave me totally something different. After removing that .lyr and starting a edit session I am able to see what you see. After clicking on points, it appears that a point layer is created based on the name of the script, for mine it was PointTestScript input(Points), temp layer?, I get options to use point or point at the end of line, I've tried both. The point doesn't do anything other then just create points in the temp layer? If I use the point at the end of line I get error, line 45, in <module>
X = row[0].lastPoint.X
AttributeError: 'NoneType' object has no attribute 'lastPoint'

Inside the database there is layers being created that correspond with the temp point layers, "Points_1", "Points_2", etc. with field names Name, Text, IntegerValue, DoubleValue and DateTime, but they are empty.

 

Also, I appreciate your help and patience.

0 Kudos
JakeSkinner
Esri Esteemed Contributor

@TonyAlmeida,

Can you post the updated code your using?

0 Kudos
TonyAlmeida
Frequent Contributor

Of course.

From what I can tell, the "polyline" doesn't get created inside the database, which is why I am getting the error. I just see "points" with the fields I mention before.

 

 

 

import arcpy
import os

# Set the workspace and target point feature class
workspace = r"C:\Temp\PointsTest.gdb"
arcpy.env.workspace = workspace
fc = "Points"
pointLayer = os.path.join(workspace, "CCAP")  # Corrected quotation marks here

# Variables
input = arcpy.GetParameterAsText(0)  # Changed variable name to follow Python conventions
Range = float(arcpy.GetParameterAsText(1))
wkid = 26770  # Corrected wkid to match the spatial reference

# Environment Variables
arcpy.env.overwriteOutput = True
arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(wkid)  # Corrected the spatial reference wkid

# Delete line_test if exists
if arcpy.Exists("line_test"):
    arcpy.Delete_management("line_test")

# Create an array to hold the point geometries
array = arcpy.Array()

# Iterate through the input features to create the polyline
with arcpy.da.SearchCursor(input, ["SHAPE@X", "SHAPE@Y"]) as cursor:
    for row in cursor:
        pt = arcpy.Point(row[0], row[1])
        array.add(pt)
del cursor

# Create the polyline from the array of points
polyline = arcpy.Polyline(array)

# Save the polyline to a file
arcpy.CopyFeatures_management(polyline, "line_test")

# Retrieve the last point's coordinates and calculate length
with arcpy.da.SearchCursor("line_test", "SHAPE@") as cursor:
    for row in cursor:
        last_point = row[0].lastPoint
        X = last_point.X
        Y = last_point.Y
        length = row[0].length / 5.28
        print("Length:", length)

# Prepare values to insert into the point layer
row_value = (length, (X, Y))

# Use arcpy.da.InsertCursor with a context manager to insert rows into the point layer
with arcpy.da.InsertCursor(pointLayer, ["SiteNum", "SHAPE@XY"]) as cursor:
    cursor.insertRow(row_value)

 

 

 

 

0 Kudos
JakeSkinner
Esri Esteemed Contributor

@TonyAlmeida ,

Make sure your map is in coordinate system (26770), then try the following:

 

import os
import arcpy

# Variables
input = arcpy.GetParameterAsText(0)
gdb = r"C:\temp\PointsTest.gdb"
wkid = 26770

# Environment Variables
arcpy.env.overwriteOutput = True
arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(wkid)

arcpy.env.workspace = gdb  

array = arcpy.Array()

with arcpy.da.SearchCursor(input, ["SHAPE@X", "SHAPE@Y"]) as cursor:
  for row in cursor:
    pt = arcpy.Point(row[0], row[1])
    array.add(pt)
del cursor

polyline = arcpy.Polyline(array)

arcpy.AddMessage("Copying line features")
arcpy.CopyFeatures_management(polyline, "line_test")

# Retrieve the last point's coordinates and calculate length
with arcpy.da.SearchCursor("line_test", "SHAPE@") as cursor:
    for row in cursor:
        last_point = row[0].lastPoint
        X = last_point.X
        Y = last_point.Y
        length = row[0].length / 5.28
        arcpy.AddMessage(f"Length:  {length}")

# Prepare values to insert into the point layer
row_value = (length, (X, Y))

# Use arcpy.da.InsertCursor with a context manager to insert rows into the point layer
arcpy.AddMessage("Inserting values into point layer")
with arcpy.da.InsertCursor("CCAP", ["SiteNum", "SHAPE@XY"]) as cursor:
    cursor.insertRow(row_value)
del cursor

 

 

0 Kudos