[GIS] Move Incorrectly Placed Address Points to Correct Parcel

addressarcgis-desktoparcmaparcpygeocoding

I'm currently in the process of cleaning up a municipality's address point data. I'm working with two data sets: address points and parcel polygons. Both the address point data and the parcel polygons have a parcel ID which allows for a spatial join and a way to verify where an address is supposed to be.

There are about 50,000 geocoded address points in my dataset. An overwhelming majority of these points fall within their corresponding parcel (aka they are spatially correct).

The problem I have is that many address points were not geocoded correctly and do not fall within their corresponding parcels. Oftentimes an address point is many parcels away from the parcel it should exist in.

Is there a a process within ArcMap (or ArcPy) that would allow me to move address points that aren't within their correct parcel polygon to where they should be? Essentially, I want to identify all of the address points that do not exist within their proper parcel (using the Parcel ID) and then redistribute those points into the parcel using the matching Parcel ID.

Best Answer

The following (untested) code should do the trick. This first gets the geometry of the points and polygons into dictionaries, then uses the polygon dictionary with the contains method of the arcpy.Polygon to test if the correct point is inside it. If not it is added to a new dict and used to update the point geometry.

import arcpy

def move_points(addresses, pt_pin_field,  parcels, parcel_pin_field,):

    # get address geometry
    with arcpy.da.SearchCursor(addresses, [pt_pin_field, 'SHAPE@']) as rows:
        point_geom = {r[0]: r[1] for r in rows}

    # get parcel geometry
    with arcpy.da.SearchCursor(parcels, [parcel_pin_field, 'SHAPE@']) as rows:
        par_geom = {r[0]: r[1] for r in rows}

    # move geometry dict
    move_dict = {}
    for pin, pt in point_geom.iteritems():
        if pin in par_geom:
            if not par_geom[pin].contains(pt):
                move_dict[pin] = par_geom[pin].centroid

    # loop through address points and move if point pin doesnt match spatial join pin
    if move_dict:
        with arcpy.da.UpdateCursor(addresses, ['SHAPE@', pt_pin_field]) as rows:
            for row in rows:
                if row[1] in move_dict:
                    row[0] = move_dict[row[1]]
                    rows.updateRow(row)           
    print 'moved {0} points'.format(len(move_dict))
    return

if __name__ == '__main__':

    addresses = r'C:\path\to\address_points.shp'
    add_pin = 'Address_PIN'
    parcel_pin = 'PID'
    parcels = r'C:\path\to\Parcels.shp'

    # run it
    move_points(addresses, add_pin, parcels, parcel_pin)
Related Question