24.5.13

Interesting Tim Peters interview...


Really enjoyed seeing the face behind the developer...



Tim Peters (core developer on CPython) discusses the early days of Python language development including meeting Guido, and porting Python to 64bit.  Additonal topics include:
  • Why people are leaving compiler development.
  • Constraints of free development time
  • Sprint-based development
  • Talks about new language features
  • Parallelism and processes

25.3.13

Point Clustering w/ ArcGIS + Python



Here is a quick example of clustering an arcgis feature class of points.  WARNING: This is more of a "gist" than a production ready version.  I am releasing it to support my presentation at the Esri Developer Summit.  Come back soon and it should be updated with additional error handling, much much more efficient use of the tools.

import json
import multiprocessing
import os
import unittest

from collections import defaultdict

import arcpy

def cluster_worker(sourcePointFeatureClass, outputDirectory, levelInfos=None, whereClause=None, workerName=None, includeLevels=None):
    print '.rectify_worker({})'.format(workerName)
    arcpy.env.overwriteOutput = True
    outputGeodatabaseName = "temp{}.gdb".format(workerName)
    outputGeodatabase = os.path.join(outputDirectory, outputGeodatabaseName)
    if arcpy.Exists(outputGeodatabase):
        arcpy.Delete_management(outputGeodatabase)
    arcpy.CreateFileGDB_management(outputDirectory, outputGeodatabaseName)
    arcpy.env.workspace = outputGeodatabase

    points_feature_layer = 'temp_points'
    points_feature_class = 'source_points'
    
    arcpy.MakeFeatureLayer_management(sourcePointFeatureClass, points_feature_layer, whereClause)
    arcpy.CopyFeatures_management(points_feature_layer, points_feature_class)

    level_infos_json = """{"origin":{"x":-20037508.342787,"y":20037508.342787},"spatialReference":{"wkid":102100},"lods":[{"level":0,"resolution":156543.033928,"scale":591657527.591555},{"level":1,"resolution":78271.5169639999,"scale":295828763.795777},{"level":2,"resolution":39135.7584820001,"scale":147914381.897889},{"level":3,"resolution":19567.8792409999,"scale":73957190.948944},{"level":4,"resolution":9783.93962049996,"scale":36978595.474472},{"level":5,"resolution":4891.96981024998,"scale":18489297.737236},{"level":6,"resolution":2445.98490512499,"scale":9244648.868618},{"level":7,"resolution":1222.99245256249,"scale":4622324.434309},{"level":8,"resolution":611.49622628138,"scale":2311162.217155},{"level":9,"resolution":305.748113140558,"scale":1155581.108577},{"level":10,"resolution":152.874056570411,"scale":577790.554289},{"level":11,"resolution":76.4370282850732,"scale":288895.277144},{"level":12,"resolution":38.2185141425366,"scale":144447.638572},{"level":13,"resolution":19.1092570712683,"scale":72223.819286},{"level":14,"resolution":9.55462853563415,"scale":36111.909643},{"level":15,"resolution":4.77731426794937,"scale":18055.954822},{"level":16,"resolution":2.38865713397468,"scale":9027.977411},{"level":17,"resolution":1.19432856685505,"scale":4513.988705},{"level":18,"resolution":0.597164283559817,"scale":2256.994353},{"level":19,"resolution":0.298582141647617,"scale":1128.497176}],"units":"esriMeters"}"""
    level_infos = json.loads(unicode(level_infos_json, 'latin-1'))
    clustered_feature_classes = []
    for level_info in level_infos['lods']:
        if not includeLevels or int(level_info['level']) in includeLevels:
            cluster_feature_class = create_cluster_layer(points_feature_class, level_info)
            if cluster_feature_class:
                clustered_feature_classes.append(os.path.join(outputGeodatabase, cluster_feature_class))
    return (clustered_feature_classes, workerName, outputGeodatabase)

def create_cluster_layer(sourcePointFeatureClass, levelInfo):
    level, res = (levelInfo['level'], levelInfo['resolution'])

    integrate_name = 'integrate_{}'.format(level)
    cluster_name = 'clusters_{}'.format(level)
    output_name = 'clusters_{}_joined'.format(level)

    if not arcpy.Exists(output_name):
        arcpy.CopyFeatures_management(sourcePointFeatureClass, integrate_name)
        distance = "{} Meters".format(res * 10)
        try:
            arcpy.Integrate_management(integrate_name, distance)
        except:
            message = arcpy.GetMessage(0)
            if 'maximum tolerance exceeded' in message.lower():
                print 'maximum tolerance exceeded: {} - {}'.format(integrate_name, distance)
                return None
            else:
                raise

        cluster_features = arcpy.CollectEvents_stats(integrate_name, cluster_name)
        add_attributes(integrate_name, cluster_name, output_name, )

        arcpy.Delete_management(integrate_name)
        arcpy.Delete_management(cluster_name)

        arcpy.AddField_management(output_name, 'zoom_level', "SHORT")
        expression = "int({})".format(levelInfo['level'])
        arcpy.CalculateField_management(output_name, 'zoom_level', expression, "PYTHON")

    return output_name

def add_attributes(integratedFeatureClass, clustersFeatureClass, outputFeatureClass, mergeNumericFields=[], mergeTextFields=False):
    '''
    TODO: Add join option for string fields; 
    TOOD: Add customizable fields for summary

    summarizes attributes for clusters
    First - The first input value is used.
    Last -The last input value is used.
    Join -Merge the values together using the delimiter value to separate the values (only valid if the output field type is text).
    Min -The smallest input value is used (only valid if the output field type is numeric).
    Max -The largest input value is used (only valid if the output field type is numeric).
    Mean -The mean is calculated using the input values (only valid if the output field type is numeric).
    Median -The median is calculated using the input values (only valid if the output field type is numeric).
    Sum -The sum is calculated using the input values (only valid if the output field type is numeric).
    StDev -The standard deviation is calculated using the input values (only valid if the output field type is numeric).
    Count -The number of values included in statistical calculations. This counts each value except null values.
    '''
    fieldmappings = arcpy.FieldMappings()
    fieldmappings.addTable(clustersFeatureClass)

    integrated_fields = arcpy.ListFields(integratedFeatureClass)
    for f in integrated_fields:
        if f.type in ['Integer', 'SmallInteger', 'Double', 'Single']:
            template = f.name + '_{}'
            for m in ['Min', 'Max', 'Mean', 'Sum', 'Count', 'StDev', 'Median']:
                fieldmap = arcpy.FieldMap()
                fieldmap.addInputField(integratedFeatureClass, f.name)
                field = fieldmap.outputField
                field.name = template.format(m.lower())
                field.aliasName = template.format(m.lower())
                fieldmap.mergeRule = m
                fieldmap.outputField = field
                fieldmappings.addFieldMap(fieldmap)

    arcpy.SpatialJoin_analysis(clustersFeatureClass, integratedFeatureClass, outputFeatureClass, "#", "#", fieldmappings)

def create_membership_where(featureClass, fieldName, values):
    where_membership = ','.join(["'{}'".format(v) for v in values])
    return arcpy.AddFieldDelimiters(featureClass, fieldName) + " IN ({})".format(where_membership)

def get_unique_field_values(featureClass, fieldName, where=None, getValueFunction=None):
    unique_values = []
    with arcpy.da.SearchCursor(featureClass, [fieldName], where) as cursor:
        for r in cursor:
            if getValueFunction:
                unique_values.append(getValueFunction(r[0]))
            else:
                unique_values.append(str(r[0]))
    final_list = list(set(unique_values))
    return final_list

all_results = []
delete_files = []
def cluster_complete(results):
    clustered_feature_classes, workerName, delete_file = results
    print 'worker complete({})'.format(workerName)

    all_results += clustered_feature_classes
    delete_files.append(delete_file)

def run_clustering(featureClass, clusterField, outputDirectory, includeLevels=None):
    global all_results
    global delete_files
    arcpy.gp.overwriteOutput = True
    pool = multiprocessing.Pool(multiprocessing.cpu_count() - 1)
    for v in get_unique_field_values(featureClass, clusterField):
        where = create_membership_where(featureClass, clusterField, [v])
        worker_arguments = (featureClass, outputDirectory, None, where, v, includeLevels)
        pool.apply_async(cluster_worker, worker_arguments, callback=cluster_complete)
    pool.close()
    pool.join()

    final_output = featureClass + '_clustered'
    arcpy.CopyFeatures_management(all_results.pop(), final_output)
    arcpy.Append_management(all_results, final_output, "TEST")

    for d in delete_files:
        arcpy.Delete_management(d)

#=============================================================================================================
# TESTING
#=============================================================================================================
class TestClustering(unittest.TestCase):
    def test_run_clustering(self):
        feature_class = r'D:\data\EPA\FRS_INTERESTS.gdb\FACILITY_INTERESTS_WM'
        clusterField = 'STATE_CODE'
        outputDirectory = r'D:\data\EPA'
        levels = range(4, 16)
        run_clustering( feature_class, clusterField, outputDirectory, includeLevels=levels )

    def _add_attributes(self):
        arcpy.gp.overwriteOutput = True
        integrated_feature_class = r'C:\gis_working_directory\tempDE.gdb\integrate_10'
        clusters = r'C:\gis_working_directory\tempDE.gdb\clusters_10'
        outputDirectory = r'C:\gis_working_directory\tempDE.gdb\clusters_10_joined'
        add_attributes(integrated_feature_class, clusters, outputDirectory)

if __name__ == '__main__':
    unittest.main()

27.2.13

Converting ArcGIS Feature Class to PyTables

Here is an example of converting a point geometry type ArcGIS Feature Class to a PyTables table. I decided to avoid using the arcpy.da.FeatureClassToNumpyArray, because I want more flexibility on how data is converted (e.g. geometry types, blobs, rasters, dates). Right now this only handles a point feature class, but I hope to add in other geometry types and make is into a module:
import arcpy
import tables
from tables import *

def feature_class_to_hdf5(featureClass, tableName, groupName='', indexFields=[]):
    '''
    TODO: add polygon and line geometry type support
    TODO: add blob field
    TODO: add raster support
    TODO: add Date logic
    '''
    try:
        #Validate feature class
        desc = arcpy.Describe(featureClass)
        if desc.dataType != 'FeatureClass':
            raise ValueError('featureClass must be a featureClass')
        if desc.featureType != 'Simple':
            raise ValueError('feature_class_to_table only support simple feature types')
        if desc.shapeType != 'Point':
            raise ValueError('sorry')

        #Construct output location for hdf5
        output_file_name = os.path.splitext(os.path.split(desc.catalogPath)[1])[0]
        if '.gdb' in desc.catalogPath:
            output_directory = os.path.split(os.path.split(desc.catalogPath)[0])[0]
        elif '.shp' in desc.catalogPath:
            output_directory = os.path.split(desc.catalogPath)[0]
        output_hd5_path = os.path.join(output_directory, output_file_name + '.h5')
        
        #Map ArcGIS Field Types to PyTable Field Types
        fields_dictionary = {}
        fields_dictionary['OID'] = Int32Col(dflt=1, pos = 0)
        fields_dictionary['X'] = Float64Col(dflt=1, pos = 1)
        fields_dictionary['Y'] = Float64Col(dflt=1, pos = 2)

        h5f_field_names = ['OID', 'X', 'Y']
        arc_field_names = ['OID@', 'SHAPE@X', 'SHAPE@Y']
        for f in desc.fields:
            if f.type in ['String','GUID']:
                fields_dictionary[str(f.name)] = StringCol(itemsize=f.length, dflt="n/a", pos=len(arc_field_names))
            elif f.type == 'Single':
                fields_dictionary[str(f.name)] = Float32Col(dflt=1, pos=len(arc_field_names))
            elif f.type == 'Double':
                fields_dictionary[str(f.name)] = Float64Col(dflt=1, pos=len(arc_field_names))
            elif f.type == 'SmallInteger':
                fields_dictionary[str(f.name)] = Int16Col(dflt=1, pos=len(arc_field_names))
            elif f.type == 'Integer':
                fields_dictionary[str(f.name)] = Int32Col(dflt=1, pos=len(arc_field_names))
            elif f.type == 'Date':
                fields_dictionary[str(f.name)] = Time64Col(dflt=1, pos=len(arc_field_names)) #Needs test...
            else:
                continue
            arc_field_names.append(str(f.name))
            h5f_field_names.append(str(f.name))

        #Create PyTables DescriptionClass
        DescriptionClass = type(tableName, (IsDescription,), fields_dictionary)

        with tables.openFile(output_hd5_path, 'w', title=featureClass) as h5_file:
            filters = Filters(complib='blosc', complevel=1)
            h5_feature_class = h5_file.createTable('/', tableName, DescriptionClass, filters=filters)
            with arcpy.da.SearchCursor(featureClass, field_names=arc_field_names) as cursor:
                for r in cursor:
                    new_row = h5_feature_class.row
                    for i, fieldName in enumerate(h5f_field_names):
                        try:
                            new_row[fieldName] = r[i]
                        except TypeError as e:
                            if 'unicode' in str(e).lower():
                                new_row[fieldName] = unicodedata.normalize('NFKD', r[i]).encode('ascii','ignore')
                            else:
                                raise
                    new_row.append()
            h5_file.flush()

    except Exception as e:
        print e
        raise

9.1.13

Funky Arcpy Cursors - functional programming examples

Python has great functional programming features with lambda expressions and first-class functions. There are many ways to cook a turkey, but here are some examples which rely heavily on a functional approach.

Map Functions to Feature Class Rows 

This example simply wraps a search cursor and executes functions. Not super helpful, but it shows the functional mindset and the use of functions as input parameters

def mapFunctionsToFeatureClassRows(featureClass, functionList):
    rows = arcpy.SearchCursor(inputFeatureClass, where)
    for r in rows:
        for f in functionList:
            f(r)
    del rows

Group Rows

def groupRows(inputFeatureClass, where, keyFunction, valueFunction):
    from collections import defaultdict

    groupings = defaultdict(list)
    rows = arcpy.SearchCursor(inputFeatureClass, where)
    for r in rows:
        groupings[keyFunction(r)].append(valueFunction(r))
    del rows
    return groupings
With 10.1+, you can utilize the arcpy.da library for significant performance increase:

def groupRows2(inputFeatureClass, fields, where, keyFunction, valueFunction):
    from collections import defaultdict
    groupings = defaultdict(list)
    with arcpy.da.SearchCursor(inputFeautreClass, fields, where) as cursor:
        for r in cursor:
            groupings[keyFunction(r)].append(valueFunction(r))
    return groupings

By convention, groupRows will call keyFunction(row) and valueFunction(row) supplying the current arcpy row object. The result will be a dictionary mapping keyFunction outputs to lists of valueFunction outputs:
if __name__ == '__main__':
    featureClass = arcpy.GetParameterAsText(0) or r'C:\python_working_directory\schools.gdb\school_points'
    schoolIdField = arcpy.GetParameterAsText(1) or r'schoolId'
    whereClause = None
    keyFunction = lambda r: r.getValue(stateSchoolId[:2])
    valueFunction = lambda r: r.getValue(stateSchoolId)

    groupsByDistrict = groupRows(features, None, keyFunction, valueFunction) 

>>> {'TX': ['TX201', 'TX202', 'TX203']}

 

Translate Append

Do you need to append to an existing feature class?  Basically the same data, but conflicting attribute tables?  Here's a functional solution:
def translateAppend(targetFeatureClass, appendFeatureClass, where, fieldTranslations, outputFeatureClass):
    import types
    
    arcpy.CopyFeatures_management(targetFeatureClass, outputFeatureClass)
    insertCursor = arcpy.InsertCursor(outputFeatureClass)
    appendRows = arcpy.SearchCursor(appendFeatureClass, where)
    
    for r in appendRows:
        newRow = insertCursor.newRow() 
        for k,v in fieldTranslations.items():
            value = None
            if isinstance(v, types.FunctionType):
                value = v(r)
            else:
                value = r.getValue(v)
            if value:
                newRow.setValue(k, value)
        insertCursor.insertRow(newRow)
    
    del newRow
    del appendRows
    del insertCursor
You supply the function with a dictionary whose keys are the fields your existing feature class. The values of the dict are functions which output the value for that field based on your new append data. The values can be lambda expressions, standard function (def statement). By convention, if you use a string value then that field from the append feature class is used:
def createGradeList(row):
    low_grade = row.getValue('low_grade')
    high_grade = row.getValue('high_grade')
    return range(low_grade, high_grade, 1)
   
#Map fields in input feature class to strings or functions
translations = {}

#Straight field-to-field name copy
translations['Shape'] = 'Shape'

#generate value using lambda expression
translations['state'] = lambda row: row.getValue(stateSchoolId)[:2] 

#lookup value in dictionary
translations['StateAbbrev'] = lambda row: state_abbrevs_index[row.getValue('FIPS')]

# generate value using function
translations['GradesCommaDelimited'] = createGradeList

translateAppend(feature_class, new_features, "1=1", translations, output_feature_class)

23.9.12

Get Unique Pixel Values By Channel (RGBA) using Python

The following code sample shows how to extract a list of unique pixel values for each channel in an image using the Python Imaging Library (PIL).

def getUniquePixelValuesByChannel(imagePath, hexBase=True):
    '''
    returns lists of unique pixel value for each channel in image(r,g,b,a)
    '''
    image = Image.open(imagePath)
    histogram = image.histogram()
    channels = [histogram[p : p + 256] for p in range(0, len(histogram), 256)]
    unique_pixels = []
    for c in channels:
        unique_pixels.append([hexBase and hex(p) or p for p in range(len(c)) if c[p] > 0])
    return unique_pixels

This function uses the image.histogram() method to get a list of the frequencies of each pixel value across each color channel (red, green, blue, alpha).  For a 4-band image, the resulting list is 1024 values long (256 for each channel) with the index of each item representing the color value in that channel.  

Below is an example of the function in-action returning the unique values for each channel in either hex or 256:

if __name__ == '__main__':

    imagePath = r'lookAtMyPixelValuesInHex.png'

    pixelValuesInHex = getUniquePixelValuesByChannel(imagePath, True)
    print 'hex result:', pixelValuesInHex
    '''
    hex result: [
        ['0x0', '0x7', '0xf', '0x17', '0x1f', '0x27', '0x37', '0xfd'], 
        ['0x7', '0xf', '0x17', '0x1f', '0x27', '0x2f', '0xfd'], 
        ['0x7', '0xf', '0x17', '0x1f', '0xfd'], 
        ['0x7', '0xf', '0x17', '0x1f', '0x27', '0x2f', '0x37', '0xfd']
    ]

    '''

    pixelValuesIn256 = getUniquePixelValuesByChannel(imagePath, False)
    print '256 color result:', pixelValuesIn256
    '''
    256 color result: [
        [0, 7, 15, 23, 31, 39, 55, 253], 
        [7, 15, 23, 31, 39, 47, 253], 
        [7, 15, 23, 31, 253], 
        [7, 15, 23, 31, 39, 47, 55, 253]
    ]
    '''


18.9.12

Normalize to USPS Street Abbreviations using Python

Here's a function i recently wrote for converting street suffixes using common street suffixes mapped to the usps standard.  You can find the list on the usps site.


Came in handy for some fuzzy matching of address strings...

def normalizeStreetSuffixes(inputValue):
        '''
        Use common abbreviations -> USPS standardized abbreviation to replace common street suffixes

        Obtains list from https://www.usps.com/send/official-abbreviations.htm
        '''
        usps_street_abbreviations = {'trpk': 'tpke', 'forges': 'frgs', 'bypas': 'byp', 'mnr': 'mnr', 'viaduct': 'via', 'mnt': 'mt',
         'lndng': 'lndg', 'vill': 'vlg', 'aly': 'aly', 'mill': 'ml', 'pts': 'pts', 'centers': 'ctrs', 'row': 'row', 'cnter': 'ctr',
          'hrbor': 'hbr', 'tr': 'trl', 'lndg': 'lndg', 'passage': 'psge', 'walks': 'walk', 'frks': 'frks', 'crest': 'crst', 'meadows': 'mdws',
           'freewy': 'fwy', 'garden': 'gdn', 'bluffs': 'blfs', 'vlg': 'vlg', 'vly': 'vly', 'fall': 'fall', 'trk': 'trak', 'squares': 'sqs',
            'trl': 'trl', 'harbor': 'hbr', 'frry': 'fry', 'div': 'dv', 'straven': 'stra', 'cmp': 'cp', 'grdns': 'gdns', 'villg': 'vlg',
             'meadow': 'mdw', 'trails': 'trl', 'streets': 'sts', 'prairie': 'pr', 'hts': 'hts', 'crescent': 'cres', 'pass': 'pass',
              'ter': 'ter', 'port': 'prt', 'bluf': 'blf', 'avnue': 'ave', 'lights': 'lgts', 'rpds': 'rpds', 'harbors': 'hbrs',
               'mews': 'mews', 'lodg': 'ldg', 'plz': 'plz', 'tracks': 'trak', 'path': 'path', 'pkway': 'pkwy', 'gln': 'gln',
                'bot': 'btm', 'drv': 'dr', 'rdg': 'rdg', 'fwy': 'fwy', 'hbr': 'hbr', 'via': 'via', 'divide': 'dv', 'inlt': 'inlt',
                 'fords': 'frds', 'avenu': 'ave', 'vis': 'vis', 'brk': 'brk', 'rivr': 'riv', 'oval': 'oval', 'gateway': 'gtwy',
                  'stream': 'strm', 'bayoo': 'byu', 'msn': 'msn', 'knoll': 'knl', 'expressway': 'expy', 'sprng': 'spg',
                   'flat': 'flt', 'holw': 'holw', 'grden': 'gdn', 'trail': 'trl', 'jctns': 'jcts', 'rdgs': 'rdgs',
                    'tunnel': 'tunl', 'ml': 'ml', 'fls': 'fls', 'flt': 'flt', 'lks': 'lks', 'mt': 'mt', 'groves': 'grvs',
                     'vally': 'vly', 'ferry': 'fry', 'parkway': 'pkwy', 'radiel': 'radl', 'strvnue': 'stra', 'fld': 'fld',
                      'overpass': 'opas', 'plaza': 'plz', 'estate': 'est', 'mntn': 'mtn', 'lock': 'lck', 'orchrd': 'orch',
                       'strvn': 'stra', 'locks': 'lcks', 'bend': 'bnd', 'kys': 'kys', 'junctions': 'jcts', 'mountin': 'mtn',
                        'burgs': 'bgs', 'pine': 'pne', 'ldge': 'ldg', 'causway': 'cswy', 'spg': 'spg', 'beach': 'bch', 'ft': 'ft',
                         'crse': 'crse', 'motorway': 'mtwy', 'bluff': 'blf', 'court': 'ct', 'grov': 'grv', 'sprngs': 'spgs',
                          'ovl': 'oval', 'villag': 'vlg', 'vdct': 'via', 'neck': 'nck', 'orchard': 'orch', 'light': 'lgt',
                           'sq': 'sq', 'pkwy': 'pkwy', 'shore': 'shr', 'green': 'grn', 'strm': 'strm', 'islnd': 'is',
                            'turnpike': 'tpke', 'stra': 'stra', 'mission': 'msn', 'spngs': 'spgs', 'course': 'crse',
                             'trafficway': 'trfy', 'terrace': 'ter', 'hway': 'hwy', 'avenue': 'ave', 'glen': 'gln',
                              'boul': 'blvd', 'inlet': 'inlt', 'la': 'ln', 'ln': 'ln', 'frst': 'frst', 'clf': 'clf',
                               'cres': 'cres', 'brook': 'brk', 'lk': 'lk', 'byp': 'byp', 'shoar': 'shr', 'bypass': 'byp',
                                'mtin': 'mtn', 'ally': 'aly', 'forest': 'frst', 'junction': 'jct', 'views': 'vws', 'wells': 'wls', 'cen': 'ctr',
                                 'exts': 'exts', 'crt': 'ct', 'corners': 'cors', 'trak': 'trak', 'frway': 'fwy', 'prarie': 'pr', 'crossing': 'xing',
                                  'extn': 'ext', 'cliffs': 'clfs', 'manors': 'mnrs', 'ports': 'prts', 'gatewy': 'gtwy', 'square': 'sq', 'hls': 'hls',
                                   'harb': 'hbr', 'loops': 'loop', 'mdw': 'mdw', 'smt': 'smt', 'rd': 'rd', 'hill': 'hl', 'blf': 'blf',
                                    'highway': 'hwy', 'walk': 'walk', 'clfs': 'clfs', 'brooks': 'brks', 'brnch': 'br', 'aven': 'ave',
                                     'shores': 'shrs', 'iss': 'iss', 'route': 'rte', 'wls': 'wls', 'place': 'pl', 'sumit': 'smt', 'pines': 'pnes',
                                      'trks': 'trak', 'shoal': 'shl', 'strt': 'st', 'frwy': 'fwy', 'heights': 'hts', 'ranches': 'rnch',
                                       'boulevard': 'blvd', 'extnsn': 'ext', 'mdws': 'mdws', 'hollows': 'holw', 'vsta': 'vis', 'plains': 'plns',
                                        'station': 'sta', 'circl': 'cir', 'mntns': 'mtns', 'prts': 'prts', 'shls': 'shls', 'villages': 'vlgs',
                                         'park': 'park', 'nck': 'nck', 'rst': 'rst', 'haven': 'hvn', 'turnpk': 'tpke', 'expy': 'expy', 'sta': 'sta',
                                          'expr': 'expy', 'stn': 'sta', 'expw': 'expy', 'street': 'st', 'str': 'st', 'spurs': 'spur', 'crecent': 'cres',
                                           'rad': 'radl', 'ranch': 'rnch', 'well': 'wl', 'shoals': 'shls', 'alley': 'aly', 'plza': 'plz', 'medows': 'mdws',
                                            'allee': 'aly', 'knls': 'knls', 'ests': 'ests', 'st': 'st', 'anx': 'anx', 'havn': 'hvn', 'paths': 'path', 'bypa': 'byp',
                                             'spgs': 'spgs', 'mills': 'mls', 'parks': 'park', 'byps': 'byp', 'flts': 'flts', 'tunnels': 'tunl', 'club': 'clb', 'sqrs': 'sqs',
                                              'hllw': 'holw', 'manor': 'mnr', 'centre': 'ctr', 'track': 'trak', 'hgts': 'hts', 'rnch': 'rnch', 'crcle': 'cir', 'falls': 'fls',
                                               'landing': 'lndg', 'plaines': 'plns', 'viadct': 'via', 'gdns': 'gdns', 'gtwy': 'gtwy', 'grove': 'grv', 'camp': 'cp', 'tpk': 'tpke',
                                                'drive': 'dr', 'freeway': 'fwy', 'ext': 'ext', 'points': 'pts', 'exp': 'expy', 'ky': 'ky', 'courts': 'cts', 'pky': 'pkwy', 'corner': 'cor',
                                                 'crssing': 'xing', 'mnrs': 'mnrs', 'unions': 'uns', 'cyn': 'cyn', 'lodge': 'ldg', 'trfy': 'trfy', 'circle': 'cir', 'bridge': 'brg',
                                                  'dl': 'dl', 'dm': 'dm', 'express': 'expy', 'tunls': 'tunl', 'dv': 'dv', 'dr': 'dr', 'shr': 'shr', 'knolls': 'knls', 'greens': 'grns',
                                                   'tunel': 'tunl', 'fields': 'flds', 'common': 'cmn', 'orch': 'orch', 'crk': 'crk', 'river': 'riv', 'shl': 'shl', 'view': 'vw',
                                                    'crsent': 'cres', 'rnchs': 'rnch', 'crscnt': 'cres', 'arc': 'arc', 'btm': 'btm', 'blvd': 'blvd', 'ways': 'ways', 'radl': 'radl',
                                                     'rdge': 'rdg', 'causeway': 'cswy', 'parkwy': 'pkwy', 'juncton': 'jct', 'statn': 'sta', 'gardn': 'gdn', 'mntain': 'mtn',
                                                      'crssng': 'xing', 'rapid': 'rpd', 'key': 'ky', 'plns': 'plns', 'wy': 'way', 'cor': 'cor', 'ramp': 'ramp', 'throughway': 'trwy',
                                                       'estates': 'ests', 'ck': 'crk', 'loaf': 'lf', 'hvn': 'hvn', 'wall': 'wall', 'hollow': 'holw', 'canyon': 'cyn', 'clb': 'clb',
                                                        'cswy': 'cswy', 'village': 'vlg', 'cr': 'crk', 'trce': 'trce', 'cp': 'cp', 'cv': 'cv', 'ct': 'cts', 'pr': 'pr', 'frg': 'frg',
                                                         'jction': 'jct', 'pt': 'pt', 'mssn': 'msn', 'frk': 'frk', 'brdge': 'brg', 'cent': 'ctr', 'spur': 'spur', 'frt': 'ft', 'pk': 'park',
                                                          'fry': 'fry', 'pl': 'pl', 'lanes': 'ln', 'gtway': 'gtwy', 'prk': 'park', 'vws': 'vws', 'stravenue': 'stra', 'lgt': 'lgt',
                                                           'hiway': 'hwy', 'ctr': 'ctr', 'prt': 'prt', 'ville': 'vl', 'plain': 'pln', 'mount': 'mt', 'mls': 'mls', 'loop': 'loop',
                                                            'riv': 'riv', 'centr': 'ctr', 'is': 'is', 'prr': 'pr', 'vl': 'vl', 'avn': 'ave', 'vw': 'vw', 'ave': 'ave', 'spng': 'spg',
                                                             'hiwy': 'hwy', 'dam': 'dm', 'isle': 'isle', 'crcl': 'cir', 'sqre': 'sq', 'jct': 'jct', 'jctn': 'jct', 'mountain': 'mtn',
                                                              'keys': 'kys', 'parkways': 'pkwy', 'drives': 'drs', 'tunl': 'tunl', 'jcts': 'jcts', 'knl': 'knl', 'center': 'ctr',
                                                               'driv': 'dr', 'tpke': 'tpke', 'sumitt': 'smt', 'canyn': 'cyn', 'ldg': 'ldg', 'harbr': 'hbr', 'rest': 'rst', 'shoars': 'shrs',
                                                                'vist': 'vis', 'gdn': 'gdn', 'islnds': 'iss', 'hills': 'hls', 'cresent': 'cres', 'point': 'pt', 'lake': 'lk', 'vlly': 'vly',
                                                                 'strav': 'stra', 'crossroad': 'xrd', 'bnd': 'bnd', 'strave': 'stra', 'stravn': 'stra', 'knol': 'knl', 'vlgs': 'vlgs',
                                                                  'forge': 'frg', 'cntr': 'ctr', 'cape': 'cpe', 'height': 'hts', 'lck': 'lck', 'highwy': 'hwy', 'trnpk': 'tpke', 'rpd': 'rpd',
                                                                   'boulv': 'blvd', 'circles': 'cirs', 'valleys': 'vlys', 'vst': 'vis', 'creek': 'crk', 'mall': 'mall', 'spring': 'spg',
                                                                    'brg': 'brg', 'holws': 'holw', 'lf': 'lf', 'est': 'est', 'xing': 'xing', 'trace': 'trce', 'bottom': 'btm',
                                                                     'streme': 'strm', 'isles': 'isle', 'circ': 'cir', 'forks': 'frks', 'burg': 'bg', 'run': 'run', 'trls': 'trl',
                                                                      'radial': 'radl', 'lakes': 'lks', 'rue': 'rue', 'vlys': 'vlys', 'br': 'br', 'cors': 'cors', 'pln': 'pln',
                                                                       'pike': 'pike', 'extension': 'ext', 'island': 'is', 'frd': 'frd', 'lcks': 'lcks', 'terr': 'ter',
                                                                        'union': 'un', 'extensions': 'exts', 'pkwys': 'pkwy', 'islands': 'iss', 'road': 'rd', 'shrs': 'shrs',
                                                                         'roads': 'rds', 'glens': 'glns', 'springs': 'spgs', 'missn': 'msn', 'ridge': 'rdg', 'arcade': 'arc',
                                                                          'bayou': 'byu', 'crsnt': 'cres', 'junctn': 'jct', 'way': 'way', 'valley': 'vly', 'fork': 'frk',
                                                                           'mountains': 'mtns', 'bottm': 'btm', 'forg': 'frg', 'ht': 'hts', 'ford': 'frd', 'hl': 'hl',
                                                                            'grdn': 'gdn', 'fort': 'ft', 'traces': 'trce', 'cnyn': 'cyn', 'cir': 'cir', 'un': 'un', 'mtn': 'mtn',
                                                                             'flats': 'flts', 'anex': 'anx', 'gatway': 'gtwy', 'rapids': 'rpds', 'villiage': 'vlg', 'flds': 'flds',
                                                                              'coves': 'cvs', 'rvr': 'riv', 'av': 'ave', 'pikes': 'pike', 'grv': 'grv', 'vista': 'vis', 'pnes': 'pnes',
                                                                               'forests': 'frst', 'field': 'fld', 'branch': 'br', 'grn': 'grn', 'dale': 'dl', 'rds': 'rds', 'annex': 'anx',
                                                                                'sqr': 'sq', 'cove': 'cv', 'squ': 'sq', 'skyway': 'skwy', 'ridges': 'rdgs', 'hwy': 'hwy', 'tunnl': 'tunl',
                                                                                 'underpass': 'upas', 'cliff': 'clf', 'lane': 'ln', 'land': 'land', 'bch': 'bch', 'dvd': 'dv', 'curve': 'curv',
                                                                                  'cpe': 'cpe', 'summit': 'smt', 'gardens': 'gdns'}
        words = inputValue.split()
        for w in words:
            if w in usps_street_abbreviations.keys():
                inputValue = inputValue.replace(w, usps_street_abbreviations[w])
        return inputValue