2

What I have:

an excel file with the names of properties within an area that is part of the PLSS in which Township/Range, Section and Lot numbers are used. The file contains a legal description for every property. Here are some examples of the individual legal descriptions:

NW/2 SW/2 - meaning the Northwest half of the Southwest half of a given section. Lots 1,2,3,4 - meaning lots numbered 1-4 of a given section. E/2 - meaning the Eastern half of a given section.

The excel data contains these fields. Township_Range, Section, Legal_Description, Property_Name

I also have the Townships/Range, Sections and Lots as separate feature classes.

What I want to do:

I would like to write a script that goes through the excel table and adds features to a feature class based on the legal descriptions of every property. I feel like this could be done by selecting the appropriate lots(divisions of each section) use arcpy.copy and append to the new feature class. However, the problem lies in creating a good way to select lots based on legal descriptions alone. There are no fields within the lot feature class that provide a legal description.

after using a search cursor to narrow down the specific twp_rng and section, It would somehow have to work like so...

if LEGAL_DESC == "E/2":
    select the lots that lie in the eastern half of the section    #How?
    copy selection
    append to new feature class

Obviously, this is a pretty dirty and could be optimized by having the legal description as an object instead of writing a separate "if" statement for each possible scenario, but that's not what i'm worried about. The logic of how to select the correct portion of lots is the problem.

Anyone have any neat ideas??

  • 1
    I actually do this right now, and I do it manually. To do it automatically, you're going to have to code something that can parse legal descriptions. There are a lot of ways to do this, and a lot of considerations. You've got your problem backward - the lot data is the atomic component of the legal, so you have to break the legal down (parse it) into something that matches the lot data for selection. That means a table that defines the E2 as the NE4 and SE4, or the SE4 and Lot 1, or the SE4 and Lots 1 and 2, or whatever else E2 could possibly mean. Plus all variants of E2, E/2, etc. – Chris W Nov 21 '14 at 22:24
  • 1
    Not only do you have to account for variations in the legal description text (everybody's shorthand seems to be different), you also have to account for the PLSS grid not being regular. Usually the legal will take that into account (ie, lots are only there because there isn't an even quarter), but sometimes land is described nominally. Meaning they might say the NW4, but what that actually means is the SE4NW4 plus lots three, four, five, and six - or any number of other possible lot numbers and combinations. – Chris W Nov 21 '14 at 22:31
  • 1
    If using the BLM data it does identify it by legal at the atomic level. Your legal descriptions are at the molecular level. The only way to do it through attribute testing is to create intermediate/molecular layers they don't provide - dissolve the lots and quarter quarters into quarters, then quarters into the four halves. Then you could select those from the new layers. To do it spatially you'll have to get section extent, divide by two (or more), test each lot/qq to find out if the majority of its extent is left/right above/below the division value, add to selection as appropriate. – Chris W Nov 22 '14 at 20:10
  • 1
    In theory you could modify your current data with new fields rather than dissolving to new shapes to indicate the quarters and halves, which might actually be simpler. Tractbuilder has the ability to do some of this. I'm not sure how much they can work with selecting existing data vs drawing new shapes, and they only do quarters, not lots. Also, NW2 usually means a diagonal half, which I've yet to see in the BLM data (not to say it couldn't be there). – Chris W Nov 22 '14 at 20:20
  • 1
    I think you should edit the script out of your question and post it as an answer instead - in this case it actually does attempt to answer the question since it didn't exist before. I for one appreciate you sharing the code. You've done in there what I suggested as modified attributes - constructing all possible molecules out of each atom. I'm not a scripter (yet) so I can't offer much feedback, but it's definitely one I'll study as I learn. – Chris W Nov 26 '14 at 21:02
1

Here is what I've come up with so far that seems to be doing the job I intended it to do.

import arcpy, math, os, sys
from arcpy import env

# Getting parameters from tool properties
excelFile = arcpy.GetParameterAsText(0)
outputFileLoc = arcpy.GetParameterAsText(1)
outputFile = arcpy.GetParameterAsText(2)
YYYYMMDD = arcpy.GetParameterAsText(3)

##################### Function Definitions ##########################################################################

# Function for building a where clause from a list object
def buildWhereClauseFromList(table, field, valueList):
    fieldDelimited = arcpy.AddFieldDelimiters(arcpy.Describe(table).path, field)
    fieldType = arcpy.ListFields(table, field)[0].type
    if str(fieldType) == 'String':
        valueList = ["'%s'" % value for value in valueList]
    whereClause = "%s IN(%s)" % (fieldDelimited, ', '.join(map(str, valueList)))
    return whereClause

# Function for generating polygons based on string type legal descriptions
def makeFeaturesFromLegalDescriptions(legalList, tempFeat_1, tempFeat_2, newFeatures, Sctn, ID, LN, TownRange, legal):
    try:
        arcpy.Select_analysis(lots, tempFeat_1, '"FRSTDIVID" = ' + "'%s'" %FDI)
        qry = buildWhereClauseFromList(tempFeat_1, "SECDIVNO", legalList)
        arcpy.Select_analysis(tempFeat_1, tempFeat_2, qry)
        arcpy.Delete_management(tempFeat_1, "")
        arcpy.Dissolve_management(tempFeat_2, tempFeat_1)
        arcpy.AddField_management(tempFeat_1, "INDEX_NUM", "LONG")
        arcpy.AddField_management(tempFeat_1, "LEGAL_DESCRIPTION", "TEXT", "", "", 50)
        curFields = ('INDEX_NUM', 'LEGAL_DESCRIPTION')
        with arcpy.da.UpdateCursor(tempFeat_1, curFields) as cursor_2:
            for row in cursor_2:
                row[0] = long(ID)
                row[1] = '%s' % ', '.join(map(str, legal))
                cursor_2.updateRow(row)
        arcpy.Append_management([tempFeat_1], newFeatures, "NO_TEST","","")
        arcpy.Delete_management(tempFeat_1, "")
        arcpy.Delete_management(tempFeat_2, "")
        arcpy.AddMessage("Feature created successfully!")
        arcpy.AddMessage("Lease #: " + str(LN))
        arcpy.AddMessage("Township/Range: " + str(TownRange))
        arcpy.AddMessage("Section: " + str(Sctn))
        arcpy.AddMessage("Legal Description: " + str(legal))
        del cursor_2
    except:
        arcpy.AddWarning("The following feature could not be created.  Please check the following information in the input table: ")
        arcpy.AddWarning("Lease #: " + str(LN))
        arcpy.AddWarning("Township/Range: " + str(TownRange))
        arcpy.AddWarning("Section: " + str(Sctn))
        arcpy.AddWarning("Legal Description: " + str(legal))

###################################################################################################################

arcpy.AddMessage(" ")
arcpy.AddWarning("Auto-digitization of legal descriptions starting now!")

# initialize objects/file paths/tables
idField = "LEG_DESC"
lotTest = "null"
outputFile = str(outputFile) + "_" + str(YYYYMMDD)
arcpy.TableToTable_conversion(excelFile, outputFileLoc, "TempTable_One")
inTable = os.path.join(outputFileLoc, "TempTable_One")
template = r'F:\DRAFTING\ArcGIS Drafting Projects\WTD\NM\_Master Data\WorkingData_John.gdb\TEMPLATE_FOR_AUTO_DIGITIZE_TOOL'
arcpy.CreateFeatureclass_management(outputFileLoc, outputFile, "POLYGON", template, "DISABLED", "DISABLED", template)
newFeat = os.path.join(outputFileLoc, outputFile)
lots = r'F:\DRAFTING\ArcGIS Drafting Projects\WTD\NM\_Master Data\SE_NM_Map.gdb\Lots'
TF1 = r'F:\DRAFTING\ArcGIS Drafting Projects\WTD\NM\_Master Data\WorkingData_John.gdb\Temp_Features_1'
TF2 = r'F:\DRAFTING\ArcGIS Drafting Projects\WTD\NM\_Master Data\WorkingData_John.gdb\Temp_Features_2'
idNum = 0
fields = arcpy.ListFields(inTable, "", "String")

# setting the ability for the script to overwrite previous outputs of the same name
arcpy.overwriteOutputs = True        
arcpy.env.overwriteOutput = True

# script for execution of tool
for field in fields:
    if field.name == str(idField):
        arcpy.AddField_management(inTable, "INDEX_NUM", "LONG")
        cursorFields = ('LEG_DESC', 'SECTION', 'TWP_RNG', 'SEC_TWP_ID', 'INDEX_NUM', 'LEASE')
        with arcpy.da.UpdateCursor(inTable, cursorFields) as cursor:
            for row in cursor:
                leaseNumber = str(row[5])
                legalDescriptions = []
                lotDesc = []
                strLegal = str(row[0])
                noSpace = row[0].replace(" ", "")
                listLD = noSpace.split(",")
                SEC = str(int(row[1]))
                section = str(int(SEC))
                if len(str(SEC)) == 1:
                    section = "0" + str(SEC)
                if len(str(SEC)) == 2:
                    section = str(SEC)
                TR = row[2]
                twpID = row[2].replace("-", "")     #Parsing the twp/rng, section identifier
                twpID = twpID.replace(" ", "")
                twpID = twpID.replace("T", "")
                twpID = twpID.replace("R", "")
                twpID = twpID.replace(",", "")
                twpID = twpID.replace("S", "0S0")
                secPart = "0E0SN" + str(section) + "0"
                twpID = twpID.replace("E", secPart)
                FDI = "NM230" + twpID
                row[4] = idNum
                row[3] = FDI
                cursor.updateRow(row)
                for legD in listLD:                                                             ###############################################
                    piece = legD.replace("Lot", "")                                             ####        
                    piece = piece.replace("s", "")                                              ###############################################                
                    legalDescriptions.append(str(piece))
                for LD in legalDescriptions:
                    if len(str(LD)) <= 2:
                        lotDesc.append(LD)
                        lotTest = "true"
                    elif len(str(LD)) > 2:
                        arcpy.AddMessage(" ")
                        cont = "true"

                        ################################### Transcriptions of Legal Descriptions ########################################################

                        #Halves - 4
                        if LD == "E1/2":
                            LegalList = ["NWNE","NENE","SWNE","SENE","NWSE","NESE","SWSE","SESE"]#
                        elif LD == "W1/2":
                            LegalList = ["NWNW","NENW","SWNW","SENW","NWSW","NESW","SWSW","SESW"]#
                        elif LD == "S1/2":
                            LegalList = ["NWSW","NESW","SWSW","SESW","NWSE","NESE","SWSE","SESE"]#
                        elif LD == "N1/2":
                            LegalList = ["NWNW","NENW","SWNW","SENW","NWNE","NENE","SWNE","SENE"]#

                        #Quarters - 4   
                        elif LD == "NW1/4":
                            LegalList = ["NWNW","NENW","SWNW","SENW"]#
                        elif LD == "NE1/4":
                            LegalList = ["NWNE","NENE","SWNE","SENE"]#
                        elif LD == "SW1/4":
                            LegalList = ["NWSW","NESW","SWSW","SESW"]#
                        elif LD == "SE1/4":
                            LegalList = ["NWSE","NESE","SWSE","SESE"]#

                        #Half Quarters - 16  
                        elif LD == "W1/2NW1/4":
                            LegalList = ["NWNW","SWNW"]#
                        elif LD == "E1/2NW1/4":
                            LegalList = ["NENW","SENW"]#
                        elif LD == "N1/2NW1/4":
                            LegalList = ["NWNW","NENW"]#
                        elif LD == "S1/2NW1/4":
                            LegalList = ["SWNW","SENW"]#

                        elif LD == "W1/2NE1/4":
                            LegalList = ["NWNE","SWNE"]#
                        elif LD == "E1/2NE1/4":
                            LegalList = ["NENE","SENE"]#
                        elif LD == "N1/2NE1/4":
                            LegalList = ["NWNE","NENE"]#
                        elif LD == "S1/2NE1/4":
                            LegalList = ["SWNE","SENE"]#

                        elif LD == "W1/2SW1/4":
                            LegalList = ["NWSW","SWSW"]#
                        elif LD == "E1/2SW1/4":
                            LegalList = ["NESW","SESW"]#
                        elif LD == "N1/2SW1/4":
                            LegalList = ["NWSW","NESW"]#
                        elif LD == "S1/2SW1/4":
                            LegalList = ["SWSW","SESW"]#

                        elif LD == "W1/2SE1/4":
                            LegalList = ["NWSE","SWSE"]#
                        elif LD == "E1/2SE1/4":
                            LegalList = ["NESE","SESE"]#
                        elif LD == "N1/2SE1/4":
                            LegalList = ["NWSE","NESE"]#
                        elif LD == "S1/2SE1/4":
                            LegalList = ["SWSE","SESE"]#

                        #Half Halves - 8   
                        elif LD == "W1/2W1/2":
                            LegalList = ["NWNW","SWNW","NWSW","SWSW"]#
                        elif LD == "E1/2W1/2":
                            LegalList = ["NENW","SENW","NESW","SESW"]#
                        elif LD == "W1/2E1/2":
                            LegalList = ["NWNE","SWNE","NWSE","SWSE"]#
                        elif LD == "E1/2E1/2":
                            LegalList = ["NENE","SENE","NESE","SESE"]#

                        elif LD == "N1/2N1/2":
                            LegalList = ["NWNW","NENW","NWNE","NENE"]#
                        elif LD == "S1/2N1/2":
                            LegalList = ["SWNW","SENW","SWNE","SENE"]#
                        elif LD == "N1/2S1/2":
                            LegalList = ["NWSW","NESW","NWSE","NESE"]#
                        elif LD == "S1/2S1/2":
                            LegalList = ["SWSW","SESW","SWSE","SESE"]#

                        #Quarter Quarters - 16
                        elif LD == "NW1/4NW1/4":
                            LegalList = ["NWNW"]#
                        elif LD == "NE1/4NW1/4":
                            LegalList = ["NENW"]#
                        elif LD == "SW1/4NW1/4":
                            LegalList = ["SWNW"]#
                        elif LD == "SE1/4NW1/4":
                            LegalList = ["SENW"]#

                        elif LD == "NW1/4NE1/4":
                            LegalList = ["NWNE"]#
                        elif LD == "NE1/4NE1/4":
                            LegalList = ["NENE"]#
                        elif LD == "SW1/4NE1/4":
                            LegalList = ["SWNE"]#
                        elif LD == "SE1/4NE1/4":
                            LegalList = ["SENE"]#

                        elif LD == "NW1/4SW1/4":
                            LegalList = ["NWSW"]#
                        elif LD == "NE1/4SW1/4":
                            LegalList = ["NESW"]#
                        elif LD == "SW1/4SW1/4":
                            LegalList = ["SWSW"]#
                        elif LD == "SE1/4SW1/4":
                            LegalList = ["SESW"]#

                        elif LD == "NW1/4SE1/4":
                            LegalList = ["NWSE"]#
                        elif LD == "NE1/4SE1/4":
                            LegalList = ["NESE"]#
                        elif LD == "SW1/4SE1/4":
                            LegalList = ["SWSE"]#
                        elif LD == "SE1/4SE1/4":
                            LegalList = ["SESE"]#

                        #All - 1
                        elif LD == "All":
                            LegalList = ["NWNE","NENE","SWNE","SENE","NWSE","NESE","SWSE","SESE","NWNW","NENW","SWNW","SENW","NWSW","NESW","SWSW","SESW","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36"]#

                        ##################################################################################################################################

                        else:
                            arcpy.AddWarning("The current legal description was not recognized!  Feature was not created.")
                            arcpy.AddWarning("Lease #: " + str(leaseNumber))
                            arcpy.AddWarning("Township/Range: " + str(TR))
                            arcpy.AddWarning("Section: " + str(section))
                            arcpy.AddWarning("Legal Description: " + str(LD))
                            cont = "false"
                        if cont == "true":
                            strLegal = str(LD)
                            makeFeaturesFromLegalDescriptions(LegalList, TF1, TF2, newFeat, section, idNum, leaseNumber, TR, LD)
                if lotTest == "true":
                    arcpy.AddMessage(" ")
                    strLegal = str(row[0])
                    makeFeaturesFromLegalDescriptions(lotDesc, TF1, TF2, newFeat, section, idNum, leaseNumber, TR, lotDesc)
                    lotTest = "false"
                idNum += 1
        arcpy.JoinField_management(newFeat, "INDEX_NUM", inTable, "INDEX_NUM")
        arcpy.DeleteField_management(newFeat, ["LEG_DESC"])
        del cursor
        arcpy.AddMessage(" ")

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.