Script works in IDE but not ArcGIS

230
4
02-01-2018 02:45 PM
JimKrumrie
New Contributor

Hello,

I've got a python script that works as expected in the PyCharm IDE but when I try to run it as a tool in #ArcCatalog 10.5 it gets about 80% done and then fails. ArcCatalog simply closes without an error message. The script attempts to create polylines between pairs of features in two point layers that are linked by a common ID field. The data I'm using are address points (1st point layer) and their geocoded locations (2nd point layer).

I've noticed that the script fails in ArcCatalog precisely when it reaches the address points in the first layer that have no matching geocoded locations in the second layer. Yet because the script works in PyCharm without issue and no error message is generated by ArcCatalog I am not sure why it fails. 

Any wisdom on this problem would be greatly appreciated. Thanx!

P.S. I've attached my script for reference.

0 Kudos
4 Replies
DanPatterson_Retired
MVP Esteemed Contributor

If it is a script in a toolbox, then add the toolbox to arctoolbox and run it from there.  I have seen cases where tools in user toolboxes fail in Catalog but work in ArcToolbox.

As for the script, you have nested try-except blocks and 2 searchcursors.  I would break up your workflow and put the pieces in functions (aka a def), gather the bits to assemble the shape and make sure that works.  nested try-except blocks aren't recommended.  In fact, I have totally given up on them since it is just as easy to trap errors before they occur in code and get a meaningful message before things fail.

0 Kudos
JimKrumrie
New Contributor

Thanx, Dan. I actually am using the ArcToolbox in ArcCatalog. Sorry that wasn't made clear.

I will fix the script as you suggest and try it again. I wondered if my rusty coding skills hadn't produced a memory or CPU-eating monster that was causing the problem for ArcGIS.

0 Kudos
DanPatterson_Retired
MVP Esteemed Contributor

Jim re-share if you get changes made.

As for running scripts in ArcToolbox... yes, just run arctoolbox from within ArcMap and not ArcCatalog... As I was alluding to, I have seen issues when toolbox is from  there that aren't present within arcmap

0 Kudos
XanderBakker
Esri Esteemed Contributor

Looking at the code, I would do a number of things differently. Some pointers below:

  • create dictionaries with the required data on the from and to points (seer lines 22 - 23)
  • include a validation to see if there are any from or to points that do not match with the to and from points resp. (see lines 25 - 33)
  • only create the output if there is any matching from and to point (line 37)
  • although I didn't do this in the code, I would also work with file geodatabase featureclasses so that you don't have to add any length fields (and due to other advantages)

BTW, I did not test the code, so there are likely errors in it. If you want, post some data and I will test to see if it works,

def main():
    import arcpy
    import os

    try:
        # Example parameters:
        # - "C:\Work\NG-911\Data\Data_Readiness\Central\swift\GIS\adp\SwiftAddressPoints_20180110.shp"
        # - "JIMS_ID"
        # - "C:\Work\NG-911\Data\Data_Readiness\Central\swift\GIS\geocoder\swift_adp_to_rcl_geocodes.shp"
        # - "JIMS_ID"
        # - "C:\temp\SwiftAddressPoints_fishbone.shp"

        from_pts = arcpy.GetParameterAsText(0)  # Shapefile/Feature Class with starting points for lines
        from_fld = arcpy.GetParameterAsText(1)  # Field in starting points table that links it to ending points table
        to_pts = arcpy.GetParameterAsText(2)  # Shapefile/Feature Class with ending points for lines
        to_fld = arcpy.GetParameterAsText(3)  # Field in ending points table that links it to starting points table
        out_file = arcpy.GetParameterAsText(4)  # Output Shapefile/Feature Class path containing lines
        fld_id = "Link_ID"


        # create dictionaries of from and to fc's
        dct_from = {r[0]: r[1] for r in arcpy.da.SearchCursor(from_pts, (from_fld, 'SHAPE@'))}
        dct_to = {r[0]: r[1] for r in arcpy.da.SearchCursor(to_pts, (to_fld, 'SHAPE@'))}

        # you could (should?) also check if all from and to points have a matching point
        from_ids = set(dct_from.keys())
        to_ids = set(dct_to.keys())
        from_dangles = list(from_ids - to_ids)
        to_dangles = list(to_ids - from_ids)
        if len(from_dangles) > 0:
            arcpy.AddWarning("From dangels:\n:{}".format(from_dangles))
        if len(to_dangles) > 0:
            arcpy.AddWarning("To dangels:\n:{}".format(to_dangles))

        # check if there are any matching from and to ids
        line_ids = list(from_ids & to_ids)
        if len(line_ids) > 0:
            # create output featureclass
            ws, fc_name = os.path.split(out_file)
            spatialRef = arcpy.Describe(from_pts).spatialReference  # Get spatial reference of starting points Shapefile/Feature Class
            arcpy.CreateFeatureclass_management(ws, fc_name, "POLYLINE", None, None, None, spatialRef)

            # add id field to output
            fld_from_id = arcpy.ListFields(from_pts, from_fld)
            AddField(out_file, fld_id, fld_from_id)

            # start insert cursor
            with arcpy.da.InsertCursor(out_file, (fld_id, 'SHAPE@')) as curs:
                for link_id in line_ids:
                    try:
                        pntg1 = dct_from[link_id]
                        pntg2 = dct_to[link_id]
                        polyline = arcpy.Polyline(arcpy.Array([pntg1.firstPoint, pntg2.firstPoint]), spatialRef)
                        curs.insertRow((link_id, polyline, ))

                    except Exception, err:
                        print err.message, link_id

            # Persist a copy of the Polyline objects using CopyFeatures
            arcpy.AddGeometryAttributes_management(out_file, "LENGTH")  # Add Length geometry field to out_file


    except Exception, err:
        print err.message  # gp.AddError(err.message)


def AddField(fc, fld_name, fld_templeate):
    arcpy.AddField_management(fc, fld_name, fld.type, fld.precision, fld.scale, fld.length)

if __name__ == '__main__':
    main()
0 Kudos