0

I have a layer of point features at fixed locations. One of the fields in my attribute table has two possible values (lets say they are "a" and "b"). How can I modify this attribute as needed in order to have a relatively even geographic distribution of "a" points and "b" points (i.e. neighbors should have alternating values as much as possible)? Picture example is below (picture is misleading, points should be in the same location).

Example Image

With a small number of points this would be easy to do visually/manually, but I am looking at a layer with up to 4000 points and would like to find a way for ArcMap to do this for me. I have looked through the various Spatial Analyst tools, and the rest of the toolbox, but I can't seem to find the right tool for the job. I'm a novice so GUI-based tools are preferred.

4
  • Welcome to GIS SE! As a new user be sure to take the Tour to learn about our focussed Q&A format.
    – PolyGeo
    Commented Feb 15, 2018 at 23:40
  • For random assignment use field calculator. If the objectID field is odd, attribute = a, if even=b Commented Feb 16, 2018 at 0:45
  • 1
    I don't think that Spatial Analyst tag is appropriate here.
    – FelixIP
    Commented Feb 16, 2018 at 2:17
  • 1
    Are the points at defined locations or have they been generated randomly?
    – Dan
    Commented Feb 16, 2018 at 2:24

2 Answers 2

1

If these points are randomly generated (e.g. for sampling and/or QA purposes) you could try running the Create Random Points tool twice, once for the "A" and once for the "B" sites, add a field and calculate to "A" / "B", then Merge the results into a single feature class. I tested this approach, generating a total of 1000 points and the results are okay but there are still isolated clusters of points with the same value. enter image description here

A second approach requires scripting in Python. Essentially the logic is

  • Choose the first feature (ObjectID = 1) in an existing feature class and set its attribute to "A"
  • Find the closest feature to ObjectID 1 and set its attribute to "B"
  • Move to that feature and find the next closest feature that hasn't been attributed and set its attribute to "A"
  • Rinse and repeat until all features have been attributed.

This method produces a better distribution of "A"s and "B"s. enter image description here

Here is the code that was used for the second method. It produces a CSV file that is used to join to the input feature class (using ObjectID as the link field).

print "Importing modules"
import sys
import arcpy
import math

#------------------------------------------------------------------------------------------
def CalcDist(fromPnt, toPnt):
    dx = toPnt[1] - fromPnt[1]
    dy = toPnt[2] - fromPnt[2]
    aDist = math.sqrt(dx ** 2 + dy ** 2)

    return aDist
#------------------------------------------------------------------------------------------

arcpy.env.overwriteOutput = True

aFC = r"C:\temp\scratch.gdb\_aaa"
startOID = 1

aSR = arcpy.Describe(aFC).spatialReference

print "Creating list of coords"

# Create a list of the Object IDs and coords
aCoords = []
aRows = arcpy.da.SearchCursor(aFC, ["OID@", "SHAPE@X", "SHAPE@Y"])
for aRow in aRows:
    aCoords.append((aRow[0], aRow[1], aRow[2]))
    if aRow[0] == startOID:
        popList = (aRow[0], aRow[1], aRow[2])
        onPoint = (aRow[0], aRow[1], aRow[2])

del aRow, aRows

# Setup output CSV file
aFile = open(r"c:\temp\AB.csv", "w")
aFile.write("OID,Recode")

print "Finding closest points and assign A and B to alternate records"
aID = "B"
aFile.write("\n" + str(startOID) + ",A")
while len(aCoords) > 1:
    aDistance = 9999999
    for aPoint in aCoords:
        if aPoint[0] <> startOID:
            aPD = CalcDist(onPoint, aPoint)

            if aPD <= aDistance:
                aDistance = aPD
                PID = aPoint[0]
                toPoint = aPoint
        else:
            popList = aPoint

    # write result to console and output file
    print "Closest OID to " + str(startOID) + " is " + str(PID)
    aFile.write("\n" + str(toPoint[0]) + "," + aID)

    # drop the point from the list and start the show again iterating onto the found point
    onPoint = toPoint
    startOID = PID
    aCoords.remove(popList)
    if aID == "A":
        aID = "B"
    else:
        aID = "A"

aFile.close()
3
  • Thanks Dan, this looks like an interesting approach. I will try this when I get back to the office.
    – Kurt S
    Commented Feb 17, 2018 at 2:03
  • I think this method works pretty well, but the nagging question in my mind is: How do I evaluate the results to determine exactly how well this method avoided clusters of A points or clusters of B points? I looked at the cluster and grouping tools, but it didn't appear that those apply to this.
    – Kurt S
    Commented Feb 20, 2018 at 20:02
  • That should probably be raised as a separate question.
    – Dan
    Commented Feb 21, 2018 at 1:37
1

You can use suggestion by @Ben S Nadler, but there is a risk of high concentration of Bs and very little As:

enter image description here

If you need them more or less near each other, this is tough. I tested grouping technique described here on 100 points and it failed for 2 groups:

enter image description here

Can be fixed manually though...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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