0

I have a city park layer that has several multi-part features. I would like to dissolve these into single part features using the Aggregate Polygon tool. I cannot use the Dissolve tool because most of these multi-parts are not directly adjacent to each other. There are highways and waterways separating the parts by up to 300'. But the individual parts have the same ParkID value, so I would like to use this field to identify the aggregated shapefile records.

Pre-Aggregate post_Aggregate

In the second image, you see green polygons that are not park of the red park. I would like to model or script a tool that would aggregate these too, based on their respective park names/IDs

  • 2
    How wide are the gaps? A quick method if you're not too fussy is to buffer out by X with dissolve by ParkID then buffer in by -X with no dissolve, where X is just over half the average gap. Most boundaries will end up exactly where they were but you will get some deformation at sharp corners and where the gap is bridged... so long as you don't look too hard it should be OK. The parks are based on cadastre so you could try to obtain the R parcels (assuming Australia, that looks like Canberra) and do polygon neighbors but that gets more complicated. – Michael Stimson Nov 6 '17 at 20:50
  • Thanks for the response. The gaps are anywhere from 20 - 500 feet. Doing this spatially I do not think is an option. My idea is to aggregate the common-named polygons. Selecting a distance would be irrelevant because only the common polygons would be aggregated at one time. This is in NYC by the way, Flushing Meadow Park – cpbride Nov 7 '17 at 21:58
  • Have a look at Aggregate Polygons resources.arcgis.com/en/help/main/10.2/index.html#//… (advanced license required) and give it a shot with both buffer out/buffer in and aggregate polygons if you have an advanced license and see if the results are suitable for you... it shouldn't take too long to perform. – Michael Stimson Nov 8 '17 at 2:40
  • Yes that is the tool I've been using but it won't aggregate according to park name. I would have to manually select all the polygons for each individual park, and iterate the process for every park in the city. – cpbride Nov 8 '17 at 2:48
  • Hmm, that is a problem, did you try buffer out/in? Do you have any experience with python? Iterating over unique values is fairly straightforward. What happens to the fields when you use Aggregate Polygon? – Michael Stimson Nov 8 '17 at 2:59
1

Here's a moderate python script that you can use, both for this task and a learning resource, it should be almost exactly what you need:

import os, sys, arcpy

InFC   = sys.argv[1] # Input feature class with parks TYPE: Feature Class
MField = sys.argv[2] # Field to dissolve by TYPE:Field, derived from InFC
OutFC  = sys.argv[3] # Output feature class TYPE: Feature Class, direction: output
AggDst = sys.argv[4] # distance for aggregation TYPE: Linear Unit

OutputCreated = False
TempDir       = os.environ.get("TEMP")              # where to put the temp stuff
TempAgg       = os.path.join(TempDir,"TempAgg.shp") # temporary aggregation

MFieldFields  = arcpy.ListFields(InFC,MField)       # check the intended fields, should be a list of 1 element
if MFieldFields == None:
    arcpy.AddError("Problem negotiating field")
    sys.exit(-1) # exit script
MField_Field = MFieldFields[0]

# count the unique values in the field, using upper case field values as keys
InFeatDict = {}

with arcpy.da.SearchCursor(InFC,MField) as SCur:
    for row in SCur: # iterate the rows (features)
        if row[0].upper() in InFeatDict:
            InFeatDict[row[0].upper()] += 1 # increment the count of this key by 1
        else:
            InFeatDict[row[0].upper()] = 1 # set the count of this new key to 1

for ThisKey in InFeatDict:
    arcpy.AddMessage("Aggregating {} for {} feature(s)".format(ThisKey,InFeatDict[ThisKey]))
    # make a feature layer with just this value
    arcpy.MakeFeatureLayer_management(InFC,"Layer","upper({}) = '{}'".format(MField,ThisKey))

    if InFeatDict[ThisKey] == 1:
        if OutputCreated:
            arcpy.Append_management("Layer",OutFC,"NO_TEST") # add the feature directly to the output
        else:
            arcpy.CopyFeatures_management("Layer",OutFC)     # export the single feature to new output
            OutputCreated = True
    else:
        # More than one feature for this key
        # get the 'real' value in the first row to calculate
        with arcpy.da.SearchCursor("Layer",MField) as sCur:
            for row in sCur:
                FieldValue = row[0]
                arcpy.AddMessage("Output will have value {}".format(FieldValue))
                break
        arcpy.AggregatePolygons_cartography("Layer",TempAgg,AggDst) # aggregation done here

        # add a field of the correct type, annoyingly the value returned by Field.type is not the
        # same as what is supplied to addfield type, the conversion follows:
        if MField_Field.type == 'String':
            arcpy.AddField_management(TempAgg,MField,"TEXT",field_length = MField_Field.length)
        elif MField_Field.type == 'Double':
            arcpy.AddField_management(TempAgg,MField,"DOUBLE")
        elif MField_Field.type == 'Integer':
            arcpy.AddField_management(TempAgg,MField,"LONG")
        elif MField_Field.type == 'SmallInteger':
            arcpy.AddField_management(TempAgg,MField,"SHORT")
        elif MField_Field.type == 'Single':
            arcpy.AddField_management(TempAgg,MField,"FLOAT")

        # calculate the field with the value before appending
        if MField_Field.type == 'String':
            arcpy.CalculateField_management(TempAgg,MField,'\"{}\"'.format(FieldValue)) # quoted for strings
        else:
            arcpy.CalculateField_management(TempAgg,MField,FieldValue) # unquoted for numbers

        # now either append or copy the temp aggregated and calculated features to the output
        if OutputCreated:
            arcpy.Append_management(TempAgg,OutFC,"NO_TEST") # add the feature directly to the output
        else:
            arcpy.CopyFeatures_management(TempAgg,OutFC)     # export the single feature to new output
            OutputCreated = True

        # Clean up
        arcpy.Delete_management(TempAgg)
    arcpy.Delete_management("Layer")

Set it up in either an existing or new toolbox (refer Adding a Script Tool from Esri help) with parameters as mentioned in the comments for the inputs like this:enter image description here

Note that the output will probably only have the one field that you specify to aggregate by.

  • Wow, that is a wonderful script, but at this time it is way over my head. Any ideas on how to approach it using the model builder? I'm going to spend time looking at this but It might take me less time to do it manually! – cpbride Nov 9 '17 at 2:56
  • I don't think modelbuilder has the scope to do this. I've tested that script and validated the outputs, it may look complicated but that's partly because I have made it general (for all field types, number of features etc..). You don't need to understand the python to use it, perhaps have a read of the Adding a script tool, copy/paste the script as written into notepad, save as type .py then add to a new toolbox. It will probably be very close to what you need. – Michael Stimson Nov 9 '17 at 3:18
  • I am experimenting with an iterator tool (iterate feature selection) which allows you to group by a field value. Seems like a step in the right direction but it still won't aggregate right – cpbride Nov 30 '17 at 19:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.