|
POST
|
For the first question you can limit the update points to those where the COUNTY name is null simply by adding a where clause to the UpdateCursor in my code: with arcpy.da.UpdateCursor(update_feature_class, ['SHAPE@', 'COUNTY'], "COUNTY IS NULL") as update_cursor: The Generate Near Table tool algorithm designed by Esri is bound to be much more efficient than any process I could develop using the distanceTo (other) method of Python geometry, since that is a fairly time consuming geometry operation. So assuming you have an Advanced license it would be easiest to run the Generate Near Tool using the default CLOSEST option and then build an attribute based dictionary from the near table result and your road features to obtain the road name to populate the points. An attribute based dictionary is incredibly fast to build and then use to perform matching between two tables and is primarily how I normally use dictionaries. I believe the following code should work once you modify the feature class paths and names and field names to match your actual data.: from time import strftime
print("Script Start: " + strftime("%Y-%m-%d %H:%M:%S"))
import arcpy
update_feature_class = r'C:\Users\tcolson\Documents\ArcGIS\Projects\MyProject\MyProject.gdb\GRSM_RESEARCH_LOCATIONS'
road_feature_class = r'C:\Users\tcolson\Documents\ArcGIS\Projects\MyProject\MyProject.gdb\Roads'
arcpy.MakeFeatureLayer_management(update_feature_class,"update_lyr","ROAD_NAME IS NULL")
arcpy.MakeFeatureLayer_management(road_feature_class,"road_lyr")
print("Created Layers: " + strftime("%Y-%m-%d %H:%M:%S"))
out_table = "C:\Users\tcolson\Documents\ArcGIS\Projects\MyProject\MyProject.gdb\points_closet_road"
if arcpy.Exists(out_table):
arcpy.Delete_management(out_table)
print("Deleted Near Table: " + strftime("%Y-%m-%d %H:%M:%S"))
arcpy.GenerateNearTable_analysis("update_lyr", "road_lyr", out_table)
print("Generated Near Table": " + strftime("%Y-%m-%d %H:%M:%S"))
roadsFieldsList = ["OID@","ROAD_NAME"]
roadsDict = {r[0]:r[1] for r in arcpy.da.SearchCursor("road_lyr", roadsFieldsList)}
matchFieldsList = ["IN_FID", "OUT_FID"]
matchDict = {r[0]:roadsDict[r[1]] for r in arcpy.da.SearchCursor(out_table, matchFieldsList)}
updateFieldsList = ["OID@", "ROAD_NAME"]
with arcpy.da.UpdateCursor("update_lyr", updateFieldsList) as updateRows:
for updateRow in updateRows:
keyValue = updateRow[0]
# verify that the keyValue is in the Dictionary
if keyValue in matchDict:
updateRow[1] = matchDict[keyValue]
updateRows.updateRow(updateRow)
print("Updated Features: " + strftime("%Y-%m-%d %H:%M:%S"))
del roadsDict
del matchDict
print("Script Finish: " + strftime("%Y-%m-%d %H:%M:%S"))
... View more
10-14-2018
05:50 PM
|
2
|
11
|
1717
|
|
POST
|
You questions is broad and vague. Are the records in the related table in a Many to One relationship with the features (Table Many Records -> Feature Class One Feature)? If so you can use a standard join in memory to view the feature attributes in the table view. If you want to create labels on the feature class features using the table attributes, which is in a One to Many or Many to Many relationship, see my Blog on Creating Labels with Related Table Data. If you want to permanently join the attributes to the table you can do a standard join and export to a new table, or add fields that match the feature class and use the techniques in my Blog on Turbo Charging Data Manipulation With Python Cursors and Dictionaries to populate the fields from the feature class. Anyway, if you mean something else, please give details.
... View more
09-28-2018
05:35 PM
|
1
|
0
|
1706
|
|
POST
|
This is next to impossible to do, at least without PhD level knowledge of the ArcGIS geometry implementation in ArcObjects,. I attempted a similar process with road casings, which are easier to do than your problem, because they have less overlap since each line is buffered by only a relatively short distance. Unfortunately, due to the fact that all of the lines that make up a polygon outline have to follow a right handed alignment I found it extremely difficult to cut the polygons at each junction. If any lines had angles in them near the junction that created artifacts that dramatically complicated the geometry even more, even if the angles were small. Most polygons could not be saved without simplifying them (a method Python does not support) and many of the simplified polygons ended up not closing the outline, which created weird behaviors and corruption in the output. I gave up after many weeks of trying to solve all of the geometry artifacts that cropped up. Python does not have half of the methods I used to make the code I developed even work half way. As far I know, no one else has posted any code that came close to doing what my code did. While I could post my code it would only work using Visual Studio and you would still have to devote many weeks of your life to solving the problems that left me stumped to make it fully work. I think you are out of luck, and at best will get suggestions for a partial solution that will still require a fair amount of manual intervention to achieve results like you have shown.
... View more
09-28-2018
09:17 AM
|
1
|
0
|
3620
|
|
POST
|
Just in case Ben deletes his post for the 4th time causing my responses to be deleted in the process I am reposting my analysis of his code outside of a response to his post. Ben's code uses a search cursor for loop on the counties and an inner update cursor for loop on the points and uses the reset method on the update cursor at the end of the inner loop to make sure his code iterates all of the feature combinations. I ran Ben's code and compared it to the code I wrote. There are several things that are critical to the way Ben's code is structured. The cursor.reset operation is time consuming, but he has minimized it by making sure that the outer loop contains the fewest features and the inner loop contains the most features. If the loops are reversed and the outer cursor contains the majority of the features, the constant repetition of the cursor reset operation in the inner loop will cause the code to slow dramatically. This is because the cursor reset is accessing the data on disk each time the cursor is reset and not caching the data in memory. On a test where I had 31 polygons and 5991 points, Ben's original code was written so that the cursor only needed to be created or reset 31 times. That code took about 7 minutes and 32 seconds to complete. However, when I reversed the loops and processed the same set of features the cursor had to be created or reset 5,991 times. Even though I added a break to the inner loop to reduce the overall number of feature comparisons being performed by about 40%, this version of the code took 37 minutes and 20 seconds to complete. The cursor reset operation clearly had a huge negative impact on the code's performance, so eliminating this operation altogether is preferable. My code does just that. With my code the loops can be in either order without impacting performance, since all of the features are read only once from disk. My code completes in about 7 minutes regardless of the order of the loops. Also, a break cannot be added to Ben's original code, since the optimal order of the loops does not allow for it. With my code the loops are reversed and I can break out of the inner county name loop when I get a name match. That reduced the time the code took to run from about 7 minutes to only 4 minutes (although the amount of time saved will vary depending on set of features being processed). Since none of my points fell on a polygon boundary this change was able to significantly reduce the processing time without having any impact on the output, and the structure of my code allows me to take advantage of it.
... View more
09-28-2018
08:27 AM
|
2
|
0
|
1717
|
|
POST
|
I would add a break after matching the first County name, since your code only stores one county name and there is no point in continuing to match county names after that. However, I think if you run my code you will find that reseting a cursor for every point is much slower than iterating over a dictionary or list which were loaded into memory by running the County cursor once. I believe the cursor reset is repeatedly accessing data on disk, which will be very time consuming in a process that grows exponentially as each new county or point is added. I would encourage you to try either of my last two code examples on your test data to see for yourself the difference that using in memory data can make. Every optimization in the part of the code that is subject to exponential growth should be considered critical to making a process like this at all scalable.
... View more
09-26-2018
10:03 PM
|
2
|
0
|
1194
|
|
POST
|
I removed that comment after examining the graphics for within and the description of intersect. It appears that within does include points that fall on the polygon boundary. I did not deal with 2 names, only because your original code would not have done that, nor would Dan's. If you wanted a list when a point fell on the boundary shared by 2 or more counties the break would have to be removed and instead of assigning the name to the point in line 35 you would append values to a list and then update the point with a string concatenating them with a character separator and always update the point. The number of comparisons will grow exponentially as the number of polygons and points grows, whereas in my previous code I minimized the number of comparisons by stopping the county iteration after the first county was matched. In this version I also sorted the names in the dictionary into a list so I only have to sort the county names once. Then instead of iterating the dictionary I can iterate that list so that when 2 or more county names touch a point the county names will be in alphabetical order. The code was already eliminating duplicate county names, so only unique county names will be listed. import arcpy
update_feature_class = r'C:\Users\tcolson\Documents\ArcGIS\Projects\MyProject\MyProject.gdb\GRSM_RESEARCH_LOCATIONS'
county_feature_class = r'C:\Users\tcolson\Documents\ArcGIS\Projects\MyProject\MyProject.gdb\CountyBoundaries'
county_count = 0
relateDict = {}
# Set up search cursor for county fc
with arcpy.da.SearchCursor(county_feature_class,['NAME','SHAPE@']) as search_cursor:
# Iterate through counties
for polygon in search_cursor:
if not polygon[0] in relateDict:
relateDict[polygon[0]] = polygon[1]
else:
relateDict[polygon[0]] = relateDict[polygon[0]].union(polygon[1])
county_names = []
for name in sorted(relateDict):
county_names.append(name)
county_count += 1
point_count = 0
comparison_count = 0
# Set up update cursor for point fc
with arcpy.da.UpdateCursor(update_feature_class, ['SHAPE@', 'COUNTY']) as update_cursor:
# Iterate through points
for point in update_cursor:
point_count += 1
name_list = []
for name in county_names:
comparison_count += 1
county = relateDict[name]
# Check if the current point is within the current polygon
if point[0].within(county): # Returns True or False
# Add the county name to the list
name_list.append(name)
point[1] = ",".join(name_list)
# Update the cursor with the new value
update_cursor.updateRow(point)
print("Processed {0} County/Point comparisons for {1} Counties and {2} Points".format(comparison_count, county_count, point_count))
... View more
09-26-2018
09:35 AM
|
0
|
14
|
1717
|
|
POST
|
Try the code below. It does not alter the schema at all and updates values in an existing field. It is also more efficient than your original approach and will minimize the number of polygon to point comparisons required to match all of the county names to the points. import arcpy
update_feature_class = r'C:\Users\tcolson\Documents\ArcGIS\Projects\MyProject\MyProject.gdb\GRSM_RESEARCH_LOCATIONS'
county_feature_class = r'C:\Users\tcolson\Documents\ArcGIS\Projects\MyProject\MyProject.gdb\CountyBoundaries'
county_count = 0
relateDict = {}
# Set up search cursor for county fc
with arcpy.da.SearchCursor(county_feature_class,['NAME','SHAPE@']) as search_cursor:
# Iterate through counties
for polygon in search_cursor:
if not polygon[0] in relateDict:
relateDict[polygon[0]] = polygon[1]
else:
relateDict[polygon[0]] = relateDict[polygon[0]].union(polygon[1])
for name in relateDict:
county_count += 1
point_count = 0
comparison_count = 0
# Set up update cursor for point fc
with arcpy.da.UpdateCursor(update_feature_class, ['SHAPE@', 'COUNTY']) as update_cursor:
# Iterate through points
for point in update_cursor:
point_count += 1
for name in relateDict:
comparison_count += 1
county = relateDict[name]
# Check if the current point is within the current polygon
if point[0].within(county): # Returns True or False
# Set the value of the field as the county name
point[1] = name
# Update the cursor with the new value
update_cursor.updateRow(point)
break
print("Processed {0} County/Point comparisons for {1} Counties and {2} Points".format(comparison_count, county_count, point_count))
... View more
09-26-2018
09:13 AM
|
1
|
20
|
2064
|
|
POST
|
You original code fails because you have not created layers for the two feature classes and are using the feature classes directly. I am surprised the select actions did not cause an error, because they only work with layers, not feature classes. The Calculate Field will always calculate every record if the input is a feature class and not a layer. Your code would work if you create layers from the feature classes and use the layers: import arcpy
update_feature_class = r'C:\Users\tcolson\Documents\ArcGIS\Projects\MyProject\MyProject.gdb\GRSM_RESEARCH_LOCATIONS'
county_feature_class = r'C:\Users\tcolson\Documents\ArcGIS\Projects\MyProject\MyProject.gdb\CountyBoundaries'
arcpy.MakeFeatureLayer_management(update_feature_class,"update_lyr")
arcpy.MakeFeatureLayer_management(county_feature_class,"county_lyr")
with arcpy.da.SearchCursor(county_feature_class,['NAME']) as cursor:
for row in cursor:
county_name = row[0]
print('Selecting '+county_name)
expression = "NAME = '"+county_name+"'"
arcpy.SelectLayerByAttribute_management ("county_lyr", "NEW_SELECTION", expression)
arcpy.SelectLayerByLocation_management("update_lyr", "INTERSECT", "county_lyr")
arcpy.CalculateField_management("update_lyr", "COUNTY","'"+county_name+"'", "PYTHON3")
... View more
09-24-2018
09:11 AM
|
2
|
1
|
2064
|
|
POST
|
The Python code is not inefficient or difficult to write and a HotKey is a poor substitute for real programming. The code below handles mxds that have group layers of any depth. The code is designed for simple feature class editing and assumes that all feature classes with selections and a STATUS field are in editable workspaces. It is not designed for editing versioned SDE feature classes, topologies, geometric networks, etc., since these are non-simple feature classes and require an active editor session to be set up to update features. import arcpy
# method recursively handles group layers
def updateLayer(layerObj,keypress):
# dictionary of values based on key entered by user
valueDict = {'e':'Existing','m':'Modified','d':'Deleted','n':'New'}
if not layerObj.isGroupLayer:
if layerObj.isFeatureLayer == True:
if len(layerObj.getSelectionSet()) > 0:
lyrFields = arcpy.ListFields(layerObj)
if 'STATUS' is lyrFields:
fields = ['STATUS']
with arcpy.da.UpdateCursor(layerObj, fields) as cursor:
for row in cursor:
if keypress.lower() in valueDict:
row[0] = valueDict[keypress.lower()]
cursor.updateRow(row)
else:
for sublayer in arcpy.mapping.ListLayers(layerObj):
if not sublayer.isGroupLayer:
updateLayer(sublayer,keypress)
# Capture the keypress value somehow. Hardcoded just to demonstrate the overall code
keypress = 'm'
mxd = arcpy.mapping.MapDocument("CURRENT") # Uses your currently open MXD
df = arcpy.mapping.ListDataFrames(mxd, '')[0] # Chooses the first dataframe
lyrs = arcpy.mapping.ListLayers(mxd, '', df)
for lyr in lyrs:
updateLayer(lyr, keypress.lower())
... View more
09-18-2018
10:01 AM
|
0
|
0
|
3213
|
|
POST
|
I agree with Joe. You are not going to avoid this. It is the nature of ArcGIS object model that you have to drill down to actual features through each individual layer and cannot access them directly from the list of layers returned by the ListLayers method or other methods that deal with the map as a whole. Even if you had access to a method that appeared to let you do this, in reality it would most likely just be hiding from you the process of iterating through the layers individually. ArcObjects had a way of accessing the selected features that are editable in an Editor session through its IEditor interface object, but I don't think Python exposes that kind of functionality. So, ArcObjects includes a much more robust object model that could have theoretically allowed you to do what you want that the Python developers chose not to incorporate. But Python itself is already a code base that has vastly simplified the automation of most common workflows and is hiding hundreds if not thousands of lines of ArcObjects code that you would have had to write if you wanted to do the same things in ArcObjects.
... View more
09-18-2018
08:42 AM
|
1
|
2
|
3213
|
|
POST
|
The issue is that line 20 does not contain layers, it contains a list of strings that represent layer names and you have no code that matches those layer names to actual layers. Also, as far as I can see your code should throw an error at line 25, because the variable lyr has never been initialized in your code. It seems like the code you posted is missing a for loop at line 24 that assigns each layer in the layers variable you created in line 19 to the lyr variable you are referencing in line 25. for lyr in layers: Line 36 should do a similar for loop and then be followed by an if statement that determines if the layer name is in the HTL_layer_list of names. Also, your code defines the HTL_ext variable, but does not define an ext variable, so I think you meant to use the HTL_ext variable throughout this section of code. This code will work: for HTL_lyr in layers:
if HTL_lyr.name in HTL_layer_list:
HTL_ext = HTL_lyr.getExtent()
if HTL_ext.XMin < xmin:
xmin = HTL_ext.XMin
if HTL_ext.YMin < ymin:
ymin = HTL_ext.YMin
if HTL_ext.XMax > xmax:
xmax = HTL_ext.XMax
if HTL_ext.YMax > ymax:
ymax = HTL_ext.YMax
... View more
09-14-2018
10:34 AM
|
2
|
1
|
3595
|
|
POST
|
It means you have to rewrite your script to be: import arcpy
arcpy.management.CopyFeatures(r"C:\CityScripts\ScheduledTasks\SafewatchCameras\Data\General.gdb\SafewatchCameras", r"C:\CityScripts\ScheduledTasks\SafewatchCameras\Data\General.gdb\SafewatchCamerastemp2", None, None, None, None) Notice I added an r in front of both your file paths. That makes it a raw string so that Python does not interpret the slashes as escape characters. The set up I recommended was taken from the Esri website that explains how to Run Standalone Scripts. It works for running scripts on my local machine.
... View more
09-14-2018
09:40 AM
|
2
|
1
|
6779
|
|
POST
|
You should be using propy.bat, not just propy. This works for me: Program/script: "c:\Program Files\ArcGIS\Pro\bin\Python\scripts\propy.bat" Add arguments (optional): "C:\Users\Owner\Documents\Test.py" I also agree that you should be using a raw string for the path and file name arguments in Python.
... View more
09-14-2018
08:15 AM
|
0
|
5
|
6779
|
|
POST
|
Which version of the script are you running? The one from the first post? I just created a brand new file geodatabase that matched the workspace path and name in the script, copied a polyline feature class into it named Centerline which had a field named STNAME containing all of the street names and ran the script from start to finish with no other set up and no problems. Based on that there is no reason for you to think that a workaround is necessary if the set up is all correct, since the code is doing nothing unusual that should break in any version of ArcMap. Your workspace must be a file geodatabase where all of your inputs and outputs will be stored, since the code was only designed to work with data in that workspace type. Are you sure that the script is set up correctly for your file geodatabase workspace, feature class names and field names? Check to see if there is a CL_INTERSECTION_POINTS point feature class in your output workspace. There could be issues if your inputs or outputs are in a feature dataset within the file geodatabase, since extra steps may have to be taken to make sure the outputs exactly match the spatial reference, coordinate domain, resolution, tolerances, etc of the feature dataset. Of course, since this is a standalone script, the script should only be run when you are sure that you don't have any active ArcMap or ArcCatalog sessions open that may point to any of the data in the output workspace, since the script needs to have exclusive lock rights to the workspace while it is running.
... View more
09-13-2018
07:59 AM
|
0
|
0
|
763
|
| Title | Kudos | Posted |
|---|---|---|
| 1 | 03-24-2026 11:37 PM | |
| 1 | 03-24-2026 08:01 PM | |
| 6 | 02-23-2026 08:34 AM | |
| 1 | 03-31-2025 03:25 PM | |
| 1 | 03-28-2025 06:54 PM |
| Online Status |
Offline
|
| Date Last Visited |
Thursday
|