I'm trying to find a geoprocessing tool like a beefed-up near analysis that will search outwards from a point (or polygon) of origin until a specified value (not distance or count) is reached. Let's say I'm using a counties feature class and specify a parameter of max 1,000,000 in the population field. The tool would begin with a user-specified county and travel outward in a radius tallying up each county's population until the parameter is met. Any ideas, either for existing tools or model-builder?
Solved! Go to Solution.
Just because it is fun...
...using this code:
import arcpy import math def main(): from collections import OrderedDict # create ordered list of distance vs id + pop fc = r"C:\Forum\NearEnhanced\gdb\pop_counties.gdb\counties" fld_pop = "PST025181D" fld_fips = "FIPS" fips_start = "29189" # "05021" max_pop = 5000000 # 1000000 # get start pnt and pop pnt_start, pop_start = getPointStart(fc, fld_fips, fld_pop, fips_start) # create ordered dict with distance, fips and pop dct_data = createDictDistancesAndData(fc, fld_fips, fld_pop, pnt_start, fips_start) # let's sort on distance to true centroid dct_sorted = OrderedDict(sorted(dct_data.items(), key=lambda x: x[1][0])) lst_fips = [fips_start] pop_tot = pop_start for oid, lst in dct_sorted.items(): pop = lst[1] pop_tot += pop if pop_tot > max_pop: print pop_tot break else: lst_fips.append(lst[2]) print "{0} in ('{1}')".format(arcpy.AddFieldDelimiters(fc, fld_fips), "','".join(lst_fips)) def getPointStart(fc, fld_fips, fld_pop, fips_start): where = "{0} = '{1}'".format(arcpy.AddFieldDelimiters(fc, fld_fips), fips_start) row = arcpy.da.SearchCursor(fc, ("SHAPE@TRUECENTROID",fld_pop), where_clause=where).next() return row[0], row[1] def createDictDistancesAndData(fc, fld_fips, fld_pop, pnt_start, fips_start): sr = arcpy.Describe(fc).spatialReference dct_data = {} where = "{0} <> '{1}'".format(arcpy.AddFieldDelimiters(fc, fld_fips), fips_start) with arcpy.da.SearchCursor(fc, ("SHAPE@TRUECENTROID",fld_pop, fld_fips, "OID@"), where_clause=where) as curs: for row in curs: pnt = row[0] pop = row[1] fips = row[2] oid = row[3] dist = getDistanceArcpy(pnt_start, pnt, sr) dct_data[oid] = [dist, pop, fips] return dct_data def getDistance(pnt_start, pnt): return math.hypot(pnt[0] - pnt_start[0], pnt[1] - pnt_start[1]) def getDistanceArcpy(pnt1, pnt2, sr): pnt_geo1 = arcpy.PointGeometry(arcpy.Point(pnt1[0], pnt1[1]), sr) pnt_geo2 = arcpy.PointGeometry(arcpy.Point(pnt2[0], pnt2[1]), sr) return pnt_geo1.distanceTo(pnt_geo2) if __name__ == '__main__': main()
The example is using county with fips 29189 and a max population of 5 million. Change lines 7 to 9 to indicate the featureclass with the counties and the names of the fips and populations fields. Change the settings on line 11 and 12 to indicate the fips to start and the maximum populations.
The result is a print of the where clause you can apply to show the counties included in the selection. The code can be adapted to create a feature class with the counties.
The code stops with the value before it passes the maximum population. This happens on line 27 to 29.
Just because it is fun...
...using this code:
import arcpy import math def main(): from collections import OrderedDict # create ordered list of distance vs id + pop fc = r"C:\Forum\NearEnhanced\gdb\pop_counties.gdb\counties" fld_pop = "PST025181D" fld_fips = "FIPS" fips_start = "29189" # "05021" max_pop = 5000000 # 1000000 # get start pnt and pop pnt_start, pop_start = getPointStart(fc, fld_fips, fld_pop, fips_start) # create ordered dict with distance, fips and pop dct_data = createDictDistancesAndData(fc, fld_fips, fld_pop, pnt_start, fips_start) # let's sort on distance to true centroid dct_sorted = OrderedDict(sorted(dct_data.items(), key=lambda x: x[1][0])) lst_fips = [fips_start] pop_tot = pop_start for oid, lst in dct_sorted.items(): pop = lst[1] pop_tot += pop if pop_tot > max_pop: print pop_tot break else: lst_fips.append(lst[2]) print "{0} in ('{1}')".format(arcpy.AddFieldDelimiters(fc, fld_fips), "','".join(lst_fips)) def getPointStart(fc, fld_fips, fld_pop, fips_start): where = "{0} = '{1}'".format(arcpy.AddFieldDelimiters(fc, fld_fips), fips_start) row = arcpy.da.SearchCursor(fc, ("SHAPE@TRUECENTROID",fld_pop), where_clause=where).next() return row[0], row[1] def createDictDistancesAndData(fc, fld_fips, fld_pop, pnt_start, fips_start): sr = arcpy.Describe(fc).spatialReference dct_data = {} where = "{0} <> '{1}'".format(arcpy.AddFieldDelimiters(fc, fld_fips), fips_start) with arcpy.da.SearchCursor(fc, ("SHAPE@TRUECENTROID",fld_pop, fld_fips, "OID@"), where_clause=where) as curs: for row in curs: pnt = row[0] pop = row[1] fips = row[2] oid = row[3] dist = getDistanceArcpy(pnt_start, pnt, sr) dct_data[oid] = [dist, pop, fips] return dct_data def getDistance(pnt_start, pnt): return math.hypot(pnt[0] - pnt_start[0], pnt[1] - pnt_start[1]) def getDistanceArcpy(pnt1, pnt2, sr): pnt_geo1 = arcpy.PointGeometry(arcpy.Point(pnt1[0], pnt1[1]), sr) pnt_geo2 = arcpy.PointGeometry(arcpy.Point(pnt2[0], pnt2[1]), sr) return pnt_geo1.distanceTo(pnt_geo2) if __name__ == '__main__': main()
The example is using county with fips 29189 and a max population of 5 million. Change lines 7 to 9 to indicate the featureclass with the counties and the names of the fips and populations fields. Change the settings on line 11 and 12 to indicate the fips to start and the maximum populations.
The result is a print of the where clause you can apply to show the counties included in the selection. The code can be adapted to create a feature class with the counties.
The code stops with the value before it passes the maximum population. This happens on line 27 to 29.
Small addition on what it actually does:
The result is printed as where clause that can be copied into the layer properties to see the result.