|
POST
|
The following changes should be made to the code above, which improves the handling of whitespace/Null name inclusion or exclusion from the name list and which makes the code work whether the name variable value you provide is a numeric ID value or a string name value. The name variable is used to create in the list of values the determines whether the point is a true intersection for that attribute (2 or more different values are listed for a point) or a pseudo-node for that attribute (only 1 value is listed for a point): I added a new boolean variable called allowWhitespace that by default is set to False. A value of False for this variable will exclude whitespace and Null name values and a value of True will include whitespace or Null name values. as a result lines 77 through 80 should now read:
# Set allowWhitespace to True to include whitespace or Null names
if allowWhitespace or (len(str(name).strip()) <> 0 and name != None):
In order to accommodate a single character list separator if you prefer to make that modification, lines 159 through 166 should read:
if names == "":
# Optional: Change separator configuration
names = opening_separator + str(name) + closing_separator
counts = opening_separator + str(valueDict[keyValue][0][name]) + closing_separator
else:
# Optional: Change separator configuration
names = names + opening_separator + str(name) + closing_separator
counts = counts + opening_separator + str(valueDict[keyValue][0][name]) + closing_separator
I revised the import code to immediately report the time the script starts and to eliminate the import of os. I also changed the double spaced blank newlines to single spaced blank newlines to reduce the overall number of lines of code and make it more compact. My latest script runs on my 120,000+ polyline test data now consistently take just under two minutes to complete. Here is the entire revised code:
# ---------------------------------------------------------------------------
# Created on: 2014-09-25
# Modified on: 2014-09-26
# Author: Richard Fairhurst
# Description: This code is designed to create intersection points, end
# points and/or pseudo-node points based on a name/ID attribute that are
# derived from an input line network. Only line end points are used
# to form intersections, unmatched line ends, or pseudo-nodes.
# High precision topology is typically required to ensure that line ends snap
# together with sufficient accuracy to be considered part of the same
# intersection point.
# ---------------------------------------------------------------------------
from time import strftime
print "Start script: " + strftime("%Y-%m-%d %H:%M:%S")
import arcpy
from arcpy import env
# Customize the workspace path to fit your data
env.workspace = r"C:\Users\Owner\Documents\ArcGIS\Centerline_Edit.gdb"
# Customize the allowWhitespace variable to False to not allow and True
# to allow whitespace or Null names/IDs to be included in the list of
# names/IDs that define the intersection.
allowWhitespace = False
# Customize the name of the field that contains networks line names/IDs
name_field = "STNAME"
# Customize the output field names for the line names list, coordinates,
# concatenated coordinates, total line count, and names count list
names_list_field = "STNAMES"
X_field = "X_COORD"
Y_field = "Y_COORD"
XY_field = "LINK_X_Y"
total_count_field = "WAYS_COUNT"
names_count_list_field = "STNAME_WAYS"
point_type_field = "POINT_TYPE"
# Customize the separators for concatenated coordinate keys and lists
opening_separator = "{"
closing_separator = "}"
# Customize the input to your feature class, shapefile or layer
inputdata = r"CENTERLINE"
inputfilefull = env.workspace + "\\" + inputdata
# Customize the output feature class or layer to fit your network
outputfile = r"CL_INTERSECTION_POINTS"
outputfilefull = env.workspace + "\\" + outputfile
# Makes a dictionary of unique string coordinate keys from both line ends
# with values in a list holding a dictionary of intersecting names with
# their counts, coordinates, and a total line count
valueDict = {}
with arcpy.da.SearchCursor(inputdata, [name_field, "SHAPE@"]) as searchRows:
for searchRow in searchRows:
name = searchRow[0]
geometry = searchRow[1]
From_X = geometry.firstPoint.X
From_Y = geometry.firstPoint.Y
To_X = geometry.lastPoint.X
To_Y = geometry.lastPoint.Y
# Customize string coordinate keys for the line From and To ends
# For Latitude and Longitude change the formating below to
# "%(OSep1)s%(FY)012.8f%(CSep1)s%(OSep2)s%(FX)012.8f%(CSep2)s" % {'OSep1': opening_separator, 'FY': From_Y, 'CSep1': closing_separator, 'OSep2': opening_separator, 'FX': From_X, 'CSep2': closing_separator}
keyValueFrom = "%(OSep1)s%(FX)012.4f%(CSep1)s%(OSep2)s%(FY)012.4f%(CSep2)s" % {'OSep1': opening_separator, 'FX': From_X, 'CSep1': closing_separator, 'OSep2': opening_separator, 'FY': From_Y, 'CSep2': closing_separator}
keyValueTo = "%(OSep1)s%(TX)012.4f%(CSep1)s%(OSep2)s%(TY)012.4f%(CSep2)s" % {'OSep1': opening_separator, 'TX': To_X, 'CSep1': closing_separator, 'OSep2': opening_separator, 'TY': To_Y, 'CSep2': closing_separator}
# Set allowWhitespace to True to include whitespace or Null names
if allowWhitespace or (len(str(name).strip()) <> 0 and name != None):
# add new From coordinate into dictionary
if not keyValueFrom in valueDict:
valueDict[keyValueFrom] = [{}, From_X, From_Y, 1]
valueDict[keyValueFrom][0][name] = 1
# add new name into names dictionary of known From coordinate
elif not name in valueDict[keyValueFrom][0].keys():
valueDict[keyValueFrom][0][name] = 1
valueDict[keyValueFrom][3] += 1
# increment counts when the From coordinate and name are known
else:
valueDict[keyValueFrom][0][name] += 1
valueDict[keyValueFrom][3] += 1
# add new To coordinates into dictionary
if not keyValueTo in valueDict:
valueDict[keyValueTo] = [{}, To_X, To_Y, 1]
valueDict[keyValueTo][0][name] = 1
# add new name into names dictionary of known To coordinate
elif not name in valueDict[keyValueTo][0].keys():
valueDict[keyValueTo][0][name] = 1
valueDict[keyValueTo][3] += 1
# increment counts when the To coordinate and name are known
else:
valueDict[keyValueTo][0][name] += 1
valueDict[keyValueTo][3] += 1
print "Finished dictionary creation: " + strftime("%Y-%m-%d %H:%M:%S")
# Determine if the outputfilefull exists already
if arcpy.Exists(outputfilefull):
# Process: Delete outputfilefull...
arcpy.Delete_management(outputfilefull, "FeatureClass")
# Process: Create Feature Class...
arcpy.CreateFeatureclass_management(env.workspace, outputfile, "POINT", "", "DISABLED", "DISABLED", inputfilefull, "", "0", "0", "0")
# Process: Add names_list_field Field...
arcpy.AddField_management(outputfilefull, names_list_field, "TEXT", "150", "", "150", "", "NULLABLE", "NON_REQUIRED", "")
# Process: Add X_field Field...
arcpy.AddField_management(outputfilefull, X_field, "DOUBLE", "", "", "", "", "NULLABLE", "NON_REQUIRED", "")
# Process: Add Y_field Field...
arcpy.AddField_management(outputfilefull, Y_field, "DOUBLE", "", "", "", "", "NULLABLE", "NON_REQUIRED", "")
# Process: Add XY_field Field...
arcpy.AddField_management(outputfilefull, XY_field, "TEXT", "", "", "28", "", "NULLABLE", "NON_REQUIRED", "")
# Process: Add total_count_field Field...
arcpy.AddField_management(outputfilefull, total_count_field, "LONG", "", "", "", "", "NULLABLE", "NON_REQUIRED", "")
# Process: Add names_count_list_field Field...
arcpy.AddField_management(outputfilefull, names_count_list_field, "TEXT", "", "", "20", "", "NULLABLE", "NON_REQUIRED", "")
# Process: Add names_count_list_field Field...
arcpy.AddField_management(outputfilefull, point_type_field, "TEXT", "", "", "22", "", "NULLABLE", "NON_REQUIRED", "")
print "Finished feature class creation: " + strftime("%Y-%m-%d %H:%M:%S")
# Create an insert cursor for a table specifying the fields that will
# have values provided
fields = [names_list_field, X_field, Y_field, XY_field, total_count_field, names_count_list_field, point_type_field, 'SHAPE@XY']
insertcursor = arcpy.da.InsertCursor(outputfilefull, fields)
# sort the string coordinate keys and process their values
for keyValue in sorted(valueDict.keys()):
# create a list of names and their counts with separator characters
# reset variables to hold new lists of names and their counts
names = ""
counts = ""
point_type = ""
# sort the names dictionary and create the names and counts lists
for name in sorted(valueDict[keyValue][0].keys()):
if names == "":
# Optional: Change separator configuration
names = opening_separator + str(name) + closing_separator
counts = opening_separator + str(valueDict[keyValue][0][name]) + closing_separator
else:
# Optional: Change separator configuration
names = names + opening_separator + str(name) + closing_separator
counts = counts + opening_separator + str(valueDict[keyValue][0][name]) + closing_separator
if closing_separator + opening_separator in names:
point_type = "True Intersection"
elif counts == opening_separator + '1' + closing_separator:
point_type = "Single-Line End Point"
elif counts == opening_separator + '2' + closing_separator:
point_type = "Pseudo Node"
else:
point_type = "Branching Lines"
row = (names, valueDict[keyValue][1], valueDict[keyValue][2], keyValue, valueDict[keyValue][3], counts, point_type, (valueDict[keyValue][1], valueDict[keyValue][2]))
insertcursor.insertRow(row)
del insertcursor
print "Finished inserting rows: " + strftime("%Y-%m-%d %H:%M:%S")
... View more
09-26-2014
06:48 AM
|
3
|
0
|
5790
|
|
POST
|
I have created a script for creating line network intersection points. It can process over 120,000 lines to create over 96,000 intersection points in approximately 2 minutes and 15 seconds. It creates a single unique point feature at any given location no matter how many line From or To ends meet at the point. It uses the provided line name/ID field's values to define a sorted unique list of names/IDs that connect at each point. The point also includes fields for its X and Y coordinates, a concatenation of the X and Y coordinates that uniquely identifies the point location and that can be used as a join field, a field listing a total line count that meets at the intersection point and a list of counts of each name ordered to match the name/ID list order. Hopefully the code comments are clear and will show you where you can customize the code to fit your preferences. By default the name/ID values that are blank strings or Null value are excluded from the name list and counts, which can make the point where the unnamed line intersects a named line appear to be a pseudo-node. The comment explain how you can change that behavior if you like. The output points can be used to identify true intersections where two or more different name/ID values meet at a point (any points where the names list field has a closing and opening separator in it or any separator in it if a single character separator is used). It also includes fields that make it possible to identify points where a single line end has no other line connected at that point, which are located at a network boundary, cul-de-sac, stub, inlet, outlet, or dangle topology error (any point where the total lines count field is 1). Finally, it includes fields that make it possible to identify attribute pseudo-nodes where only a single name/ID value occurs where two or more lines join together (any point which has a total lines count greater than 1 and the name list field has no closing and opening separator in it or no separator at all if a single character separator is used). These different classes of points can be selected and exported to create separate point feature classes if you like. Here is the code: # ---------------------------------------------------------------------------
# Created on: 2014-09-25
# Author: Richard Fairhurst
# Description: This code is designed to create intersection points, end
# points and/or pseudo-node points based on a name/ID attribute that are
# derived from an input line network. Only line end points are used
# to form intersections, unmatched line ends, or pseudo-nodes.
# High precision topology is typically required to ensure that line ends snap
# together with sufficient accuracy to be connsidered part of the same
# intersection point.
# ---------------------------------------------------------------------------
import arcpy
import os
from arcpy import env
from time import strftime
print "Start script: " + strftime("%Y-%m-%d %H:%M:%S")
# Customize the workspace path to fit your data
env.workspace = r"C:\Users\Owner\Documents\ArcGIS\Centerline_Edit.gdb"
env.overwriteOutput = True
# Customize the name of the field that contains networks line names/IDs
name_field = "STNAME"
# Customize the output field names for the line names list, coordinates,
# concatenated coordinates, total line count, and names count list
names_list_field = "STNAMES"
X_field = "X_COORD"
Y_field = "Y_COORD"
XY_field = "LINK_X_Y"
total_count_field = "WAYS_COUNT"
names_count_list_field = "STNAME_WAYS"
# Customize the separators for concatenated coordinate keys and lists
opening_separator = "{"
closing_separator = "}"
# Customize the input to your feature class, shapefile or layer
inputdata = r"CENTERLINE"
inputfilefull = env.workspace + "\\" + inputdata
# Customize the output feature class or layer to fit your network
outputfile = r"CL_INTERSECTION_POINTS"
outputfilefull = env.workspace + "\\" + outputfile
# Makes a dictionary of unique string coordinate keys from both line ends
# with values in a list holding a dictionary of intersecting names with
# their counts, coordinates, and a total line count
valueDict = {}
with arcpy.da.SearchCursor(inputdata, [name_field, "SHAPE@"]) as searchRows:
for searchRow in searchRows:
name = searchRow[0]
geometry = searchRow[1]
From_X = geometry.firstPoint.X
From_Y = geometry.firstPoint.Y
To_X = geometry.lastPoint.X
To_Y = geometry.lastPoint.Y
# Customize string coordinate keys for the line From and To ends
# For Latitude and Longitude change the formating below to
# "%(OSep1)s%(FY)012.8f%(CSep1)s%(OSep2)s%(FX)012.8f%(CSep2)s" % {'OSep1': opening_separator, 'FY': From_Y, 'CSep1': closing_separator, 'OSep2': opening_separator, 'FX': From_X, 'CSep2': closing_separator}
keyValueFrom = "%(OSep1)s%(FX)012.4f%(CSep1)s%(OSep2)s%(FY)012.4f%(CSep2)s" % {'OSep1': opening_separator, 'FX': From_X, 'CSep1': closing_separator, 'OSep2': opening_separator, 'FY': From_Y, 'CSep2': closing_separator}
keyValueTo = "%(OSep1)s%(TX)012.4f%(CSep1)s%(OSep2)s%(TY)012.4f%(CSep2)s" % {'OSep1': opening_separator, 'TX': To_X, 'CSep1': closing_separator, 'OSep2': opening_separator, 'TY': To_Y, 'CSep2': closing_separator}
# Intersection names normally exclude unnamed and Null name lines
# Optional: If you want unnamed lines to create intersection points
# comment out the next line and dedent the if clause regions below
if name <> " " and name != None:
# add new From coordinate into dictionary
if not keyValueFrom in valueDict:
valueDict[keyValueFrom] = [{}, From_X, From_Y, 1]
valueDict[keyValueFrom][0][name] = 1
# add new name into names dictionary of known From coordinate
elif not name in valueDict[keyValueFrom][0].keys():
valueDict[keyValueFrom][0][name] = 1
valueDict[keyValueFrom][3] += 1
# increment counts when the From coordinate and name are known
else:
valueDict[keyValueFrom][0][name] += 1
valueDict[keyValueFrom][3] += 1
# add new To coordinates into dictionary
if not keyValueTo in valueDict:
valueDict[keyValueTo] = [{}, To_X, To_Y, 1]
valueDict[keyValueTo][0][name] = 1
# add new name into names dictionary of known To coordinate
elif not name in valueDict[keyValueTo][0].keys():
valueDict[keyValueTo][0][name] = 1
valueDict[keyValueTo][3] += 1
# increment counts when the To coordinate and name are known
else:
valueDict[keyValueTo][0][name] += 1
valueDict[keyValueTo][3] += 1
print "Finished dictionary creation: " + strftime("%Y-%m-%d %H:%M:%S")
# Determine if the outputfilefull exists already
if arcpy.Exists(outputfilefull):
# Process: Delete outputfilefull...
arcpy.Delete_management(outputfilefull, "FeatureClass")
# Process: Create Point Feature Class and use the input spatial reference...
arcpy.CreateFeatureclass_management(env.workspace, outputfile, "POINT", "", "DISABLED", "DISABLED", inputfilefull, "", "0", "0", "0")
# Process: Add names_list_field Field (customize the field length if you need to)...
arcpy.AddField_management(outputfilefull, names_list_field, "TEXT", "", "", "150", "", "NULLABLE", "NON_REQUIRED", "")
# Process: Add X_field Field...
arcpy.AddField_management(outputfilefull, X_field, "DOUBLE", "", "", "", "", "NULLABLE", "NON_REQUIRED", "")
# Process: Add Y_field Field...
arcpy.AddField_management(outputfilefull, Y_field, "DOUBLE", "", "", "", "", "NULLABLE", "NON_REQUIRED", "")
# Process: Add XY_field Field (customize the field length if you need to)...
arcpy.AddField_management(outputfilefull, XY_field, "TEXT", "", "", "28", "", "NULLABLE", "NON_REQUIRED", "")
# Process: Add total_count_field Field...
arcpy.AddField_management(outputfilefull, total_count_field, "LONG", "", "", "", "", "NULLABLE", "NON_REQUIRED", "")
# Process: Add names_count_list_field Field (customize the field length if you need to)...
arcpy.AddField_management(outputfilefull, names_count_list_field, "TEXT", "", "", "20", "", "NULLABLE", "NON_REQUIRED", "")
print "Finished feature class creation: " + strftime("%Y-%m-%d %H:%M:%S")
# Create an insert cursor for a table specifying the fields that will
# have values provided
fields = [names_list_field, X_field, Y_field, XY_field, total_count_field, names_count_list_field, 'SHAPE@XY']
insertcursor = arcpy.da.InsertCursor(outputfilefull, fields)
# sort the string coordinate keys and process their values
for keyValue in sorted(valueDict.keys()):
# create a list of names and their counts with separator characters
# reset variables to hold new lists of names and their counts
names = ""
counts = ""
# sort the names dictionary and create the names and counts lists
for name in sorted(valueDict[keyValue][0].keys()):
if name == "":
# Optional: Change separator configuration
names = opening_separator + name + closing_separator
counts = opening_separator + str(valueDict[keyValue][0][name]) + closing_separator
else:
# Optional: Change separator configuration
names = names + opening_separator + name + closing_separator
counts = counts + opening_separator + str(valueDict[keyValue][0][name]) + closing_separator
row = (names, valueDict[keyValue][1], valueDict[keyValue][2], keyValue, valueDict[keyValue][3], counts, (valueDict[keyValue][1], valueDict[keyValue][2]))
insertcursor.insertRow(row)
# create output feature classes/shapefiles
# two names separated by "}{" are a real intersection
# if closing_separator + opening_separator in names:
# write to text file
# f.write('"' + names + '",' + str(valueDict[keyValue][1]) + ',' + str(valueDict[keyValue][2]) + ',"' + keyValue + '",' + str(valueDict[keyValue][3]) + ',"' + counts + '"\n' )
# one name with a total line count of 1 is an unmatched line end, which
# may be a stub, a cul-de-sac, or a topology error for a road network
# one name with a total line count > 1 is a pseudo node for the name
print "Finished inserting rows: " + strftime("%Y-%m-%d %H:%M:%S") Here is a picture of what the field data looks like for the code as written: OBJECTID * Shape * STNAMES X_COORD Y_COORD LINK_X_Y WAYS_COUNT STNAME_WAYS 50676 Point {MADRONO CT} 6296418.904414 2243614.433358 {6296418.9044}{2243614.4334} 1 {1} 50867 Point {10TH ST} 6297064.90509 2243667.729839 {6297064.9051}{2243667.7298} 2 {2} 50889 Point {10TH ST}{WOLFSKILL AVE} 6297145.870479 2243668.425376 {6297145.8705}{2243668.4254} 2 {1}{1} 50997 Point {BELL AVE}{WOLFSKILL AVE} 6297360.565588 2243670.268876 {6297360.5656}{2243670.2689} 3 {1}{2} 51160 Point {MAGNOLIA AVE}{WOLFSKILL AVE} 6297781.153002 2243671.327601 {6297781.1530}{2243671.3276} 4 {2}{2} 51407 Point {HANSEN AVE}{WOLFSKILL AVE} 6298405.94145 2243672.728517 {6298405.9415}{2243672.7285} 4 {2}{2} 51724 Point {6TH ST}{WOLFSKILL AVE} 6299040.417543 2243674.151414 {6299040.4175}{2243674.1514} 4 {2}{2} 52871 Point {5TH ST}{WOLFSKILL AVE} 6301563.821945 2243679.94635 {6301563.8219}{2243679.9464} 4 {2}{2} 53020 Point {MIKE LN}{WOLFSKILL AVE} 6301904.892129 2243680.73211 {6301904.8921}{2243680.7321} 3 {1}{2} 53164 Point {HAVENHURST DR}{WOLFSKILL AVE} 6302189.418463 2243681.387948 {6302189.4185}{2243681.3879} 3 {1}{2} 53355 Point {WOLFSKILL AVE} 6302528.110371 2243682.16813 {6302528.1104}{2243682.1681} 2 {2} 53469 Point {BLUEBONNET RD}{WOLFSKILL AVE} 6302822.667853 2243682.846935 {6302822.6679}{2243682.8469} 3 {1}{2} 53637 Point {POPPY RD}{WOLFSKILL AVE} 6303293.806346 2243683.932563 {6303293.8063}{2243683.9326} 3 {1}{2} 53810 Point {4TH ST}{CORSO ALTO AVE}{WOLFSKILL AVE} 6303774.846395 2243684.940106 {6303774.8464}{2243684.9401} 3 {1}{1}{1} 50553 Point {MADRONO CT}{YUCCA AVE} 6295970.886609 2243832.745913 {6295970.8866}{2243832.7459} 3 {1}{2} 50586 Point {10TH ST}{YUCCA AVE} 6296116.882052 2244123.025844 {6296116.8821}{2244123.0258} 4 {2}{2} 50779 Point {WILDFIRE CIR} 6296733.251882 2244238.88388 {6296733.2519}{2244238.8839} 1 {1} 50638 Point {WILDFIRE CIR}{YUCCA AVE} 6296284.789853 2244458.484163 {6296284.7899}{2244458.4842} 4 {2}{2} 50332 Point {10TH ST}{ARMANDO DR} 6295320.524393 2244504.723572 {6295320.5244}{2244504.7236} 3 {2}{1} 50534 Point {WILDFIRE CIR} 6295917.833565 2244566.072531 {6295917.8336}{2244566.0725} 1 {1} 53872 Point {YUCCA AVE} 6303868.692664 2244646.38405 {6303868.6927}{2244646.3840} 2 {2} 53854 Point {4TH ST}{YUCCA AVE} 6303843.885627 2244649.336472 {6303843.8856}{2244649.3365} 4 {2}{2} 50223 Point {10TH ST}{LAKEVIEW AVE} 6294916.12986 2244698.551596 {6294916.1299}{2244698.5516} 4 {2}{2} 53027 Point {MIKE LN} 6301917.13784 2244702.955131 {6301917.1378}{2244702.9551} 1 {1} 50855 Point {DEBBIE LN} 6297012.336626 2244807.570407 {6297012.3366}{2244807.5704} 1 {1} 50737 Point {DEBBIE LN}{WOLFGRAM DR}{YUCCA AVE} 6296566.133122 2245018.051942 {6296566.1331}{2245018.0519} 4 {1}{1}{2} Here is a picture of one of the ways this data can displayed:
... View more
09-25-2014
11:13 PM
|
4
|
10
|
12550
|
|
POST
|
To eliminate the steps that make the gridcode list unique and sorted the following code, which changes line 9 to eliminate the unique gridcode process and line 15 to eliminate the sorted gridcode process, would work. The string list of gridcodes could technically be built in lines 8-10 allowing lines 14-19 to be eliminated with a minor tweak to line 21 if you don't need them sorted, but this structure makes it easy to add back the processes that make the list either unique or sorted. The previous code would make the gridcode list for ID 47991 be "3, 7, 9", while this code will make ID 47991 have the list "9, 7, 3".
# get a dictionary of unique ID value keys and each key's gridcodes
valueDict = {}
with arcpy.da.SearchCursor(inputshp, ["Id", "GRIDCODE"]) as searchRows:
for searchRow in searchRows:
keyValue = searchRow[0]
gridcode = searchRow[1]
if not keyValue in valueDict:
valueDict[keyValue] = [gridcode]
else:
valueDict[keyValue].append(gridcode)
# sort the ID value keys and convert the gridcodes to a string list
for keyValue in sorted(valueDict.keys()):
items = ""
for item in valueDict[keyValue]:
if items == "":
items = str(item)
else:
items = items + ", " + str(item)
# write to text file with the gridcode list enclosed in double quotes
f.write(str(keyValue) + ',"' + items + '"\n' )
... View more
09-24-2014
08:56 AM
|
1
|
8
|
2547
|
|
POST
|
The code is untested, so if it throws an error it is probably because I missed some minor syntax requirement. The overall logic structure should be correct. I drew upon this stackoverflow post and this Geonet post to come up with this code. It assumed that both the IDs and the listed gridcodes for each ID should be unique and sorted. If you wanted only the IDs unique and sorted and the listed items to allow repeated values or to be in their original order, the code would have to be modified slightly.
... View more
09-23-2014
08:19 AM
|
0
|
1
|
2547
|
|
POST
|
The code to get the unique list of ID values and a concatenated list of GRIDCODE values is done with the search cursor outputting to a dictionary. The dictionary key makes sure that you will get a unique list of ID values, If you need the values sorted, then the dictionary does not do that unless you put it through a secondary process.
# get a dictionary of unique ID value keys and each key's unique gridcodes
valueDict = {}
with arcpy.da.SearchCursor(inputshp, ["Id", "GRIDCODE"]) as searchRows:
for searchRow in searchRows:
keyValue = searchRow[0]
gridcode = searchRow[1]
if not keyValue in valueDict:
valueDict[keyValue] = [gridcode]
elif not gridcode in valueDict[keyValue]:
valueDict[keyValue].append(gridcode)
# sort both the ID value keys and the gridcodes which are converted to a string list
for keyValue in sorted(valueDict.keys()):
items = ""
for item in sorted(valueDict[keyValue]):
if items == "":
items = str(item)
else:
items = items + ", " + str(item)
# write to text file with the gridcode list enclosed in double quotes
f.write(str(keyValue) + ',"' + items + '"\n' )
... View more
09-23-2014
07:49 AM
|
0
|
12
|
2547
|
|
POST
|
Tony: Trying to change the code doesn't offend me. However, I want you to realize that the code was designed to solve a specific problem and, from what you have told me, it appears you are now solving a different problem that requires some fundamental changes to the overall code design. I cannot offer advice on why the original code does not work for the new problem without first analyzing and understanding how this new problem is both similar to and different from the original problem. So look at each of the 7 items I described as a requirement of the new problem and tell me if I have correctly or incorrectly described what the code revisions must accomplish. If all problems could simply be reversed than there would be only trivial differences between the recipe for taking a carton of eggs to make scrambled eggs and the recipe for taking scrambled eggs to fill a carton of eggs. But the differences between the problems that have to be solved by those two recipes far outweigh any similarities. That is an intentionally extreme example. But i hope it makes it clear that before code designed to solve one problem is used to solve its opposite problem that the differences between the two problems must be limited and relatively trivial to make that adaptation. While your problem is not unsolvable like the second half of the scrambled egg problem, based on the differences I have outlined it is not a trivial change that you are making.
... View more
09-19-2014
11:46 AM
|
0
|
0
|
1005
|
|
POST
|
Tony: Of course it is freaking out. It was not designed to do what you want and the logic fundamentally is altered if you start with a selection of objects that need data written to them rather than a selection of objects with data to read. Getting the first half of the code to work is the simplistic case and easy to reverse, it is the second half of the code where the complexities of the problem come into play and a simple reversal of code will never work. You cannot simply reverse the process unless the address points are the data source with values to read and the parcels are the update target with fields to be written. As long as the parcels have the data, you have to obtain a parcel selection one way or another and read it before you process the addresses, because you have to read the values you want to transfer and then update the features with blank fields. Stop hacking the code. You don't understand why I organized it the way I did if you think it can just be reversed. It is dependent on several conditions that your data does not meet. To have any hope of getting useful code you have to define the data you are starting with and what you want to end up with. I gather you have the following: 1. The parcels have the data you want to read (the same as the data the original code was based on) 2. The address points are the target that need fields completed from parcel data.(the same as the data the original code was based on) 3. You want to select addresses instead of parcels (opposite the original data not only in terms of the object type, but also in terms of switching the selection from the data source to the data target) 4. One address point can only fall within one parcel. (True of the original data and why it was easy to get the first half of the code to work where only one address, and therefore only one parcel, is involved). 5. You only want to write to the selected address points. (not at all a requirement of the original code.) 6. You cannot assume there is a parcel covering all of the address points you selected that have data to transfer (not a consideration with the original data, and this condition could still result in a failure in the first half of the code as well as the second half) 7. You don't want to select the same parcel over and over if it covers many address points (easily done with the original data, not easily done with your data). As you can see it is not a simple reversal of the code. New errors and inefficiencies could arise with this scenario that were not a factor with the original code. The code structure has to be rethought. Before I can rethink it I need to verify all of the statements I have outlined are true, so that I can include all of the new error checks I now have to handle and the most efficient steps that will meet all of the new requirements of the problem.
... View more
09-18-2014
03:48 PM
|
0
|
2
|
1005
|
|
POST
|
Make line 63 in the posted code above identical to line 21 since I presume you want to edit the same workspace in the lower part of the code as you are in the upper part of the code. It looks like your actual line numbers have changed slightly, so it may be line 64 or 65 in your most current code.
... View more
09-18-2014
10:29 AM
|
0
|
4
|
1779
|
|
POST
|
Originally this code was written with versioned data in mind and the code enabled undo and kept the editor session open. The user was required to save the edits. But you are doing unversioned edits, so you may as well close the edit session and save the edits. That way you don't have to manually do the save step and you don't have the ability to undo edits anyway. So add this after lines 48 and 96 edit.stopEditing(True)
... View more
09-18-2014
10:07 AM
|
0
|
6
|
1779
|
|
POST
|
That is a new one. I have heard of the error where it won't let you use an Update cursor outside of an edit session, but not where it was prohibited by an edit session. Does it let you process the cursor outside of an edit session (comment out all editor lines which all have the edit variable in them)? Is it SDE data? If so is it versioned or unversioned? If it is unversioned SDE data then you have to change line 28 and line 70 to: edit.startEditing(False) Is the address point layer a simple point layer? Is it associated with a topology, geometric network, or some other complex layer type? You have to look at exactly what configuration of database and feature class this data is set up to be in order to figure out how to structure this code. It was written with only SDE versioned simple feature classes.in mind. Other data configurations may need other code configurations. There is also a chance that the editor should be tested to see if it is already open for editing before attempting to open this data for editing. You did not set up an edit session in ArcMap before you ran this code I hope.
... View more
09-17-2014
04:24 PM
|
0
|
8
|
1779
|
|
POST
|
So what else does the error say, other than that it occurred on line 42? An error normally would say something about the cause, like invalid use of unassigned variables, lack of edit rights, or some other problem that caused the error.
... View more
09-17-2014
01:19 PM
|
0
|
0
|
1779
|
|
POST
|
Your ACCOUNT variable name is not consistently using the same upper case spelling. It does not matter that the spelling changes in the data sources for the field, this variable must have the same spelling when being read and being assigned between the two data sources. Try changing line 43 from: singleRow[0] = Account To singleRow[0] = ACCOUNT Also, are you certain that at least one address point exists within the parcel you chose as your test case?
... View more
09-17-2014
12:53 PM
|
0
|
0
|
1779
|
|
POST
|
When you say the the combination of the fields in the two different tables is not showing up, do you mean that only the fields of the original layer that existed before the join are in the table view, or do you mean that you see the field names of the joined table, but all of the records have Null values in those fields? If you see the joined table fields, but all of the records have Null values in those fields, be sure that the format of the actual values that are supposed to be joined are identical on both data sources, including the presence or absence of leading or trailing white space characters, capitalization, dashes/special characters, etc. If your table is an Excel Spreadsheet you should convert it to a real table with ObjectIDs. ObjectIDs in a data source are required to get the full range of behaviors that are possible when using a join. Creating joins with a data source that has no ObhectIDs, like and Excel Spreadsheet, result in huge limitations on what behaviors are supported.
... View more
09-17-2014
11:46 AM
|
0
|
0
|
1191
|
|
POST
|
I assume the original Area is taken from the layer that had the clipping boundary before the intersect. Was there a single polygon for each physically separate area in that layer? Are you absolutely positive that there are no sliver polygons in the output due to imprecise topology? Anywhere sliver polygons occur they would double the area calculated within them. Did you use a course tolerance setting during any geoprocessing operations? The greater the tolerance setting the more the features shapes can be altered by moving or dropping vertices. Try a complete Dissolve of the intersected features with no attributes to create a single output polygon. If the area of that dissolved shape is significantly larger than the original then something changed the projection or the original shape. If it is nearly the same then the difference is due to sliver polygons. Do not expect an exact match in the before and after area numbers if you do the dissolve, because each geopricessing operation (intersect and dissolve) results in slight alterations to the shape geometry within your tolerance and resolution settings.
... View more
09-17-2014
08:45 AM
|
0
|
0
|
2585
|
|
POST
|
in all likelihood the leading zeros are not part of the field value if the fields are numeric and are just a display property of the layer that has no effect on the values you are concatenating. If they are real numeric values and not string values then in order to replicate the layer display properties in the concatenated string values the formula in VB Script needs to be: Left("00", 2 - Len([field1])) & [field1] & Left("000", 3 - Len([field2])) & [field2]
... View more
09-17-2014
05:57 AM
|
0
|
0
|
1689
|
| Title | Kudos | Posted |
|---|---|---|
| 1 | 03-24-2026 11:37 PM | |
| 1 | 03-24-2026 08:01 PM | |
| 7 | 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 |
07-09-2026
12:59 AM
|