diff --git a/PGM_Toolbox_ArcPro3/PGM_Toolbox.pyt b/PGM_Toolbox_ArcPro3/PGM_Toolbox.pyt
new file mode 100644
index 0000000000000000000000000000000000000000..77593fe7e694c35d3dea377c23965dc60e151328
--- /dev/null
+++ b/PGM_Toolbox_ArcPro3/PGM_Toolbox.pyt
@@ -0,0 +1,880 @@
+#/******************************************************************************
+# * $Id$
+# *
+# * Project:  Support a range of common workflows to support planetary geologic mapping
+# * Purpose:  Tools include Merge Files, New Template Geodatabase, Build Polygons,
+#             Topology Check, Longitude Conversion.
+# * Author:   Marc Hunter, mahunter@usgs.gov
+# *
+# ******************************************************************************
+# * Public domain licenes (unlicense)
+# *
+# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# * DEALINGS IN THE SOFTWARE.
+# ******************************************************************************
+# * Disclaimer
+# * 
+# * This software has been approved for release by the U.S. Geological Survey (USGS). 
+# * Although the software has been subjected to rigorous review, the USGS reserves 
+# * the right to update the software as needed pursuant to further analysis and review. 
+# * No warranty, expressed or implied, is made by the USGS or the U.S. Government as 
+# * to the functionality of the software and related material nor shall the fact of 
+# * release constitute any such warranty. Furthermore, the software is released on 
+# * condition that neither the USGS nor the U.S. Government shall be held liable for 
+# * any damages resulting from its authorized or unauthorized use.
+# *****************************************************************************/
+
+import arcpy, os, sys
+from arcpy import env
+from arcpy.sa import *
+import numpy
+import pandas
+#import json
+
+class Toolbox(object):
+    def __init__(self):
+        """Define the toolbox (the name of the toolbox is the name of the
+        .pyt file)."""
+        self.label = "PyGM Toolbox"
+        self.alias = ""
+
+# List of tool classes associated with this toolbox
+        self.tools = [ClipNShip, LongitudeConversion, NewGDB, MergeFiles, TopologyCheck, BuildPolygons]
+
+class ClipNShip(object):
+    def __init__(self):
+        """Define the tool (tool name is the name of the class)."""
+        self.label = "Basemap Clip-N-Ship"
+        self.canRunInBackground = False
+
+    def getParameterInfo(self):
+        """Define parameter definitions"""
+        param0 = arcpy.Parameter(
+            displayName="Boundary Feature",
+            name="in_fc",
+            datatype= ["GPFeatureLayer", "DEFeatureClass"],
+            parameterType="Required",
+            direction="Input")
+        param1 = arcpy.Parameter(
+            displayName="Mosaic Raster List",
+            name="mosaic_list",
+            datatype=["DERasterDataset", "GPRasterLayer"],
+            parameterType="Required",
+            direction="Input")
+        param2 = arcpy.Parameter(
+            displayName="Output Raster",
+            name="out_raster",
+            datatype="DERasterDataset",
+            parameterType="Required",
+            direction="Output")
+        param3 = arcpy.Parameter(
+            displayName="Workspace",
+            name="workspace",
+            datatype="DEWorkspace",
+            parameterType="Required",
+            direction="Input")
+
+        param1.multiValue = True
+        parameters = [param0, param1, param2, param3]
+        return parameters
+
+    def isLicensed(self):
+        """Set whether tool is licensed to execute."""
+        return True
+
+    def updateParameters(self, parameters):
+        """Modify the values and properties of parameters before internal
+        validation is performed.  This method is called whenever a parameter
+        has been changed."""
+        return
+
+    def updateMessages(self, parameters):
+        """Modify the messages created by internal validation for each tool
+        parameter.  This method is called after internal validation."""
+        return
+
+
+    def execute(self, parameters, messages):
+#read in parameters
+        inFC = parameters[0].value
+        rasterList = parameters[1].values
+        outRaster = parameters[2].valueAsText
+        inWorkspace = parameters[3].valueAsText
+
+        inFCdesc = arcpy.Describe(inFC)
+        FCsr = inFCdesc.spatialReference
+
+        ws = inWorkspace + "\\clipNship"
+        tempRaster = ws + "\\tempRaster.tif"
+        inRasterSR = arcpy.Describe(rasterList[0]).spatialReference
+        gdbPath = ws + "\\temp.gdb"
+        if gdbPath:
+            arcpy.Delete_management(ws)
+        os.mkdir(inWorkspace + "\\clipNship")
+   
+        if len(rasterList) > 1:
+            arcpy.CreateFileGDB_management(ws, "temp", "CURRENT")
+            arcpy.CreateMosaicDataset_management(gdbPath, "tempMosaic", inRasterSR)
+            mosaicPath = gdbPath + "\\tempMosaic"
+            for ea in rasterList:
+                arcpy.AddRastersToMosaicDataset_management(mosaicPath, "Raster Dataset", ea)
+            clippedMosaic = arcpy.sa.ExtractByMask(mosaicPath, inFC)
+            clippedMosaic.save(tempRaster)
+            arcpy.ProjectRaster_management(tempRaster, outRaster, FCsr)
+            arcpy.Delete_management(ws)
+
+        if len(rasterList) == 1:
+            clippedRaster = arcpy.sa.ExtractByMask(rasterList[0], inFC)
+            clippedRaster.save(tempRaster)
+            arcpy.ProjectRaster_management(tempRaster, outRaster, FCsr)
+            arcpy.AddMessage(ws)
+            arcpy.Delete_management(tempRaster)
+            arcpy.Delete_management(ws)
+        return
+
+class LongitudeConversion(object):
+    def __init__(self):
+        """Define the tool (tool name is the name of the class)."""
+        self.label = "Longitude Conversion"
+        self.description = ""
+        self.canRunInBackground = False
+
+    def getParameterInfo(self):
+        """Define parameter definitions"""
+        param0 = arcpy.Parameter(
+            displayName="Input Feature Class",
+            name="in_fc",
+            datatype= ["GPFeatureLayer", "DEFeatureClass"],
+            parameterType="Required",
+            direction="Input")
+        param1 = arcpy.Parameter(
+            displayName="Output Feature Class",
+            name="out_fc",
+            datatype="DEFeatureClass",
+            parameterType="Required",
+            direction="Output")
+        param2 = arcpy.Parameter(
+            displayName="Conversion Type",
+            name="conversion_type",
+            datatype="GPString",
+            parameterType="Required",
+            direction="Input")
+			
+        param2.filter.type = "ValueList"
+        param2.filter.list = ["Convert to -180 - 180 longitude", "Convert to 0 - 360 longitude"]
+        parameters = [param0, param1, param2]
+        return parameters
+
+    def isLicensed(self):
+        """Set whether tool is licensed to execute."""
+        return True
+
+    def updateParameters(self, parameters):
+        """Modify the values and properties of parameters before internal
+        validation is performed.  This method is called whenever a parameter
+        has been changed."""
+        return
+
+    def updateMessages(self, parameters):
+        """Modify the messages created by internal validation for each tool
+        parameter.  This method is called after internal validation."""
+        return
+
+
+    def execute(self, parameters, messages):
+#read in parameters
+        inFC = parameters[0].value
+        outFC = parameters[1].valueAsText
+        conversionType = parameters[2].valueAsText
+        inFCdesc = arcpy.Describe(inFC)
+        FCsr = inFCdesc.spatialReference
+        if FCsr.projectionName:
+            arcpy.AddMessage("Projected Coordinate Systems are not supported")
+            return
+        arcpy.Delete_management("in_memory")
+		
+        if conversionType == "Convert to -180 - 180 longitude":
+            pMeridian = 0.0
+            lonChange = -360.0
+            oldMeridian = "180.0],UNIT"
+            newMeridian = "0.0],UNIT"
+            clipArrayWest = arcpy.Array([arcpy.Point(0.0, 90.0),
+                    arcpy.Point(0.0, -90.0),
+                    arcpy.Point(180.0, -90.0),
+                    arcpy.Point(180.0, 90.0),
+					arcpy.Point(0.0, 90.0)])
+            clipArrayEast = arcpy.Array([arcpy.Point(180.0, 90.0),
+                    arcpy.Point(180.0, -90.0),
+                    arcpy.Point(360.0, -90.0),
+                    arcpy.Point(360.0, 90.0),
+					arcpy.Point(180.0, 90.0)])
+        if conversionType == "Convert to 0 - 360 longitude":
+            pMeridian = 180.0
+            lonChange = 360.0
+            oldMeridian = "0.0],UNIT"
+            newMeridian = "180.0],UNIT"
+            clipArrayWest = arcpy.Array([arcpy.Point(-180.0, 90.0),
+                    arcpy.Point(-180.0, -90.0),
+                    arcpy.Point(0.0, -90.0),
+                    arcpy.Point(0.0, 90.0),
+					arcpy.Point(-180.0, 90.0)])
+            clipArrayEast = arcpy.Array([arcpy.Point(0.0, 90.0),
+                    arcpy.Point(0.0, -90.0),
+                    arcpy.Point(180.0, -90.0),
+                    arcpy.Point(180.0, 90.0),
+					arcpy.Point(0.0, 90.0)])
+
+        outFClayer = "in_memory/outFClayer"
+        arcpy.CopyFeatures_management(inFC, outFClayer)
+        clipPolygonLayer = "in_memory/polygonFC"
+        arcpy.CreateFeatureclass_management("in_memory", "polygonFC", "Polygon")
+        arcpy.DefineProjection_management(clipPolygonLayer, FCsr)
+        clipPolygonWest = arcpy.Polygon(clipArrayWest)
+        clipPolygonEast = arcpy.Polygon(clipArrayEast)
+        polyCursor = arcpy.da.InsertCursor(clipPolygonLayer, ['SHAPE@'])
+        polyCursor.insertRow([clipPolygonWest])
+        polyCursor.insertRow([clipPolygonEast])
+        del polyCursor
+
+        clipFClayer = "in_memory/clipFClayer"
+        arcpy.AddField_management(outFClayer, "orig_fid", "LONG")
+        arcpy.CalculateField_management(outFClayer, "orig_fid", '!FID!', "PYTHON")
+        arcpy.Intersect_analysis([[clipPolygonLayer, 2], [outFClayer, 1]], clipFClayer)
+        FCsrPRJ = FCsr.exportToString()
+        outFCsrPRJ = FCsrPRJ.replace(oldMeridian, newMeridian)
+        outFCsr = arcpy.SpatialReference()
+        outFCsr.loadFromString(outFCsrPRJ)
+        arcpy.DefineProjection_management(clipFClayer, outFCsr)
+
+# Convert X coordinate of each point in each part in each feature
+        with arcpy.da.UpdateCursor(clipFClayer, ['SHAPE@']) as cursor:
+            pnt_array = arcpy.Array()
+            part_array = arcpy.Array()
+            partnum = 0
+            arcpy.AddMessage(arcpy.Describe(clipFClayer).shapeType)
+# For points
+            if arcpy.Describe(clipFClayer).shapeType == "Point":
+                for row in cursor:
+                    feature = arcpy.PointGeometry.getPart(row[0])
+                    if feature.X < 0.0 or feature.X > 180.0:
+                        feature.X = feature.X + lonChange
+                    else:
+                        pass
+                    part_array.add(feature)
+                    cursor.updateRow(row)
+# For multipoints
+            elif arcpy.Describe(clipFClayer).shapeType == "Multipoint":
+                for row in cursor:
+                    geometry = row[0]
+                    for part in geometry:
+                        pts = geometry[0]
+                        if pts.X < 0.0 or pts.X > 180.0:
+                            pts.X = pts.X + lonChange
+                        else:
+                            pass
+                        part_array.add(pts)
+                    cursor.updateRow(row)
+                    part_array.removeAll()
+                    partnum += 1
+# For polylines and polygons					
+            else:
+                for row in cursor:
+                    geometry = row[0]
+                    for part in geometry:
+                        pts = geometry.getPart(partnum)
+                        for pnt in pts:
+                            if pnt:
+                                if pnt.X < 0.0 or pnt.X > 180.0:
+                                    pnt.X = pnt.X + lonChange
+                                else:
+                                    pass
+                                pnt_array.add(pnt)
+                        part_array.add(pnt_array)
+                        arcpy.AddMessage(pnt_array)
+                        pnt_array.removeAll()
+                        partnum += 1
+                    arcpy.AddMessage(part_array)
+                    if arcpy.Describe(clipFClayer).shapeType == "Polygon":
+                        new_feature_geometry = arcpy.Polygon(part_array)
+                    elif arcpy.Describe(clipFClayer).shapeType == "Polyline":
+                        new_feature_geometry = arcpy.Polyline(part_array)
+                    else:
+                        print("Unrecognized geometry")
+                        return
+                    part_array.removeAll()
+                    geometry = new_feature_geometry
+                    cursor.updateRow(row)
+
+        if conversionType == "Convert to -180 - 180 longitude":
+#Should snap be applied both ways?
+            arcpy.Snap_edit(clipFClayer, [[clipFClayer, "VERTEX", "0.001 Degrees"]])
+
+        dissolveFClayer = "in_memory/dissolveFClayer"
+        arcpy.Dissolve_management(clipFClayer, dissolveFClayer, 'orig_fid')
+        joinedFC = arcpy.JoinField_management(dissolveFClayer, 'orig_fid', outFClayer, 'orig_fid')
+        arcpy.CopyFeatures_management(joinedFC, outFC)
+        arcpy.DeleteField_management(outFC, ['orig_fid', 'orig_fid_1'])
+        arcpy.Delete_management("in_memory")
+        return
+
+class NewGDB(object):
+    def __init__(self):
+        """Define the tool (tool name is the name of the class)."""
+        self.label = "New Map GDB"
+        self.description = ""
+        self.canRunInBackground = False
+
+    def getParameterInfo(self):
+        """Define parameter definitions"""
+        param0 = arcpy.Parameter(
+            displayName="Map Boundary",
+            name="inBoundary",
+            datatype=["GPFeatureLayer", "DEShapefile", "DEFeatureClass"],
+            parameterType="Required",
+            direction="Input")
+        param1 = arcpy.Parameter(
+            displayName="Output Location",
+            name="outWorkspace",
+            datatype="DEWorkspace",
+            parameterType="Required",
+            direction="Input")
+        param2 = arcpy.Parameter(
+            displayName="File Geodatabase Name",
+            name="outGDB",
+            datatype="GPString",
+            parameterType="Required",
+            direction="Input")
+        param3 = arcpy.Parameter(
+            displayName="Geodatabase Version",
+            name="GDBversion",
+            datatype="GPString",
+            parameterType="Required",
+            direction="input")
+        param0.Filter = ["Polygon"]
+        param3.filter.type = "ValueList"
+        param3.filter.list = ["9.2", "9.3", "10.0", "CURRENT"]
+        param3.value = "CURRENT"
+        params = [param0, param1, param2, param3]
+        return params
+
+    def isLicensed(self):
+        """Set whether tool is licensed to execute."""
+        return True
+
+    def updateParameters(self, parameters):
+        """Modify the values and properties of parameters before internal
+        validation is performed.  This method is called whenever a parameter
+        has been changed."""
+        return
+
+    def updateMessages(self, parameters):
+        """Modify the messages created by internal validation for each tool
+        parameter.  This method is called after internal validation."""
+        return
+
+    def execute(self, params, messages):
+        """The source code of the tool."""
+        #Read in parameters
+        inBoundary = params[0].valueAsText
+        outWorkspace = params[1].valueAsText
+        GDBname = params[2].valueAsText
+        gdbVersion = params[3].valueAsText
+        #Get boundary object
+        boundaryDesc = arcpy.Describe(inBoundary)
+        boundaryFullPath = boundaryDesc.catalogPath
+        boundarySpatialRef = boundaryDesc.spatialReference
+	
+        #Create file GDB in workspace
+        arcpy.CreateFileGDB_management(outWorkspace, GDBname, gdbVersion)
+	
+        #Get GDB object
+        GDBpath = outWorkspace + "/" + GDBname + ".gdb"
+        GDBdesc = arcpy.Describe(GDBpath)
+	
+        #Create domains
+        arcpy.CreateDomain_management(GDBpath, "contact", "type of geologic contact", "TEXT", "CODED", "DEFAULT", "DEFAULT")
+        arcpy.CreateDomain_management(GDBpath, "contacts, key beds, and dikes", "contacts, key beds, and dikes", "TEXT", "CODED", "DEFAULT", "DEFAULT")
+        arcpy.CreateDomain_management(GDBpath, "eolian features", "eolian features", "TEXT", "CODED", "DEFAULT", "DEFAULT")
+        arcpy.CreateDomain_management(GDBpath, "faults", "faults", "TEXT", "CODED", "DEFAULT", "DEFAULT")
+        arcpy.CreateDomain_management(GDBpath, "folds", "folds", "TEXT", "CODED", "DEFAULT", "DEFAULT")
+        arcpy.CreateDomain_management(GDBpath, "landslide and mass-wasting", "landslide and mass-wasting", "TEXT", "CODED", "DEFAULT", "DEFAULT")
+        arcpy.CreateDomain_management(GDBpath, "lineaments and joints", "lineaments and joints", "TEXT", "CODED", "DEFAULT", "DEFAULT")
+        arcpy.CreateDomain_management(GDBpath, "linear feature", "type of linear feature", "TEXT", "CODED", "DEFAULT", "DEFAULT")
+        arcpy.CreateDomain_management(GDBpath, "point feature", "type of point feature", "TEXT", "CODED", "DEFAULT", "DEFAULT")
+        arcpy.CreateDomain_management(GDBpath, "polygon feature", "type of polygon feature", "TEXT", "CODED", "DEFAULT", "DEFAULT")
+        arcpy.CreateDomain_management(GDBpath, "zones and blocks", "zones and blocks", "TEXT", "CODED", "DEFAULT", "DEFAULT")
+
+        #Attribute coded domain dictionaries
+        contactDict = {"certain":"certain", "approximate":"approximate", "inferred":"inferred", "concealed":"concealed", "gradational":"gradational", "boundary":"boundary", "generic contact 1":"generic contact 1", "generic contact 2":"generic contact 2"}
+        contactsdikesDict = {"contact 1.1.1":"contact - id certain, location accurate", "contact 1.1.2":"contact - id queried, location accurate", "contact 1.1.3":"contact - id certain, location approximate", "contact 1.1.4":"contact - id queried, location approximate", "contact 1.1.5":"contact - id certain, location inferred", "contact 1.1.6":"contact - id queried, location inferred", "contact 1.1.7":"contact - id certain, location concealed", "contact 1.1.8":"contact - id queried, location concealed", "contact 1.1.25":"unconformable contact - id certain, location accurate", "contact 1.1.26":"unconformable contact - id queried, location accurate", "contact 1.1.27":"unconformable contact - id certain, location approximate", "contact 1.1.28":"unconformable contact - id queried, location approximate", "contact 1.1.29":"unconformable contact - id certain, location inferred", "contact 1.1.30":"unconformable contact - id queried, location inferred", "contact 1.1.31":"unconformable contact - id certain, location concealed", "contact 1.1.32":"unconformable contact - id queried, location concealed", "dike 1.3.5":"dike - id certain, location accurate", "dike 1.3.6":"dike - id certain, location approximate"}
+        eolianDict = {"dune crest":"dune crest", "scarp on dune screst":"scarp on dune screst"}
+        faultsDict = {"fault 2.1.1":"fault, generic - id certain, location accurate", "fault 2.1.2":"fault, generic - id queried, location accurate", "fault 2.1.3":"fault, generic - id certain, location approximate", "fault 2.1.4":"fault, generic - id queried, location approximate", "fault 2.1.5":"fault, generic - id certain, location inferred", "fault 2.1.6":"fault, generic - id queried, location inferred", "fault 2.1.7":"fault, generic - id certain, location concealed", "fault 2.1.8":"fault, generic - id queried, location concealed", "fault 2.2.1":"fault, normal - id certain, location accurate", "fault 2.2.3":"fault, normal - id certain, location approximate", "fault 2.2.5":"fault, normal - id certain, location inferred", "fault 2.2.7":"fault, normal - id certain, location concealed", "fault 2.8.1":"thrust fault - id certain, location accurate", "fault 2.8.3":"thrust fault - id certain, location approximate", "fault 2.8.5":"thrust fault - id certain, location inferred", "fault 2.8.7":"thrust fault - id certain, location concealed", "fault 2.9.1":"overturned thrust fault - id certain, location accurate", "fault 2.9.3":"overturned thrust fault - id certain, location approximate", "fault 2.9.5":"overturned thrust fault - id certain, location inferred", "fault 2.9.7":"overturned thrust fault - id certain, location concealed", "fault 2.11.10":"fault - vertical or near vertical", "fault 2.14.1":"ductile shear zone"}
+        foldsDict = {"anticline 5.1.1":"anticline - id certain, location accurate", "anticline 5.1.3":"anticline - id certain, location approximate", "anticline 5.1.5":"anticline - id certain, location inferred", "anticline 5.1.7":"anticline - id certain, location concealed", "antiform 5.2.9":"antiform - id certain, location accurate", "antiform 5.2.11":"antiform - id certain, location approximate", "antiform 5.2.13":"antiform - id certain, location inferred", "antiform 5.2.15":"antiform - id certain, location concealed", "anticline 5.3.17":"anticline, overturned - id certain, location accurate", "anticline 5.3.19":"anticline, overturned - id certain, location approximate", "anticline 5.3.21":"anticline, overturned - id certain, location inferred", "anticline 5.3.23":"anticline, overturned - id certain, location concealed", "syncline 5.5.1":"syncline - id certain, location accurate", "syncline 5.5.3":"syncline - id certain, location approximate", "syncline 5.5.5":"syncline - id certain, location inferred", "syncline 5.5.7":"syncline - id certain, location concealed", "synform 5.6.9":"synform - id certain, location accurate", "synform 5.6.11":"synform - id certain, location approximate", "synform 5.6.13":"synform - id certain, location inferred", "synform 5.6.15":"synform - id certain, location concealed", "syncline 5.7.17":"syncline, overturned - id certain, location accurate", "syncline 5.7.19":"syncline, overturned - id certain, location approximate", "syncline 5.7.21":"syncline, overturned - id certain, location inferred", "syncline 5.7.23":"syncline, overturned - id certain, location concealed", "monocline 5.9.1":"monocline - id certain, location accurate", "monocline 5.9.3":"monocline - id certain, location approximate", "monocline 5.9.5":"monocline - id certain, location inferred", "monocline 5.9.7":"monocline - id certain, location concealed", "anticline 5.10.5":"anticline, plunging", "anticline 5.10.6":"anticline, doubly plunging", "syncline 5.10.7":"syncline, plunging", "syncline 5.10.8":"syncline, doubly plunging"}
+        landslideDict = {"landslide 17.1":"slip surface outline - id certain, location accurate", "landslide 17.2":"slip surface outline - id queried, location accurate", "landslide 17.3":"slip surface outline - id certain, location approximate", "landslide 17.4":"slip surface outline - id queried, location approximate", "landslide 17.5":"slip surface outline - id certain, location inferred", "landslide 17.6":"slip surface outline - id queried, location inferred", "landslide 17.7":"slip surface outline - id certain, location concealed", "landslide 17.8":"slip surface outline - id queried, location concealed", "landslide 17.12":"main scarp, active", "landslide 17.13":"main scarp, inactive", "landslide 17.16":"minor scarp, active", "landslide 17.17":"minor scarp, inactive", "landslide 17.20":"main toe, active", "landslide 17.21":"main toe, inactive", "landslide 17.22":"minor toe, internal thrust, or pressure ridge, active", "landslide 17.23":"minor toe, internal thrust, or pressure ridge, inactive", "landslide 17.35":"tension crack", "landslide 17.44":"crest line of lateral levee", "landslide 17.10":"direction of downslope motion"}
+        lineamentDict = {"lineament 4.1.1":"lineament", "joint 4.2.1":"joint, id certain, location accurate", "joint 4.2.2":"joint, id certain, location approximate", "joint 4.2.3":"joint, vertical or subvertical"}
+        linearDict = {"fault, certain":"fault, certain", "fault, approx.":"fault, approx.", "normal fault, certain":"normal fault, certain", "normal fault, approx.":"normal fault, approx.", "graben trace, certain":"graben trace, certain", "graben trace, approx.":"graben trace, approx.", "reverse fault, certain":"reverse fault, certain", "reverse fault, approx.":"reverse fault, approx.", "ridge crest (type 1), certain":"ridge crest (type 1), certain", "ridge crest (type 1), approx.":"ridge crest (type 1), approx.", "ridge crest (type 2), certain":"ridge crest (type 2), certain", "ridge crest (type 2), approx.":"ridge crest (type 2), approx.", "scarp base":"scarp base", "scarp crest":"scarp crest", "groove":"groove", "depression margin":"depression margin", "dome margin":"dome margin", "lineament":"lineament", "channel (fluvial)":"channel (fluvial)", "channel (volcanic)":"channel (volcanic)", "sinuous channel or groove":"sinuous channel or groove", "crest of crater rim":"crest of crater rim", "crest of buried crater":"crest of buried crater", "small crater rim":"small crater rim", "generic linear 1":"generic linear 1", "generic linear 2":"generic linear 2", "trough (type 1)":"trough (type 1)", "trough (type 2)":"trough (type 2)"}
+        pointDict = {"pitted cone":"pitted cone", "small crater":"small crater", "shield (certain)":"shield (certain)", "shield (uncertain)":"shield (uncertain)", "generic pt 1":"generic pt 1", "generic pt 2":"generic pt 2"}
+        polygonDict = {"dark-colored ejecta":"dark-colored ejecta", "secondary crater chain":"dark-colored mantling material", "thumbprint terrain":"thumbprint terrain", "polygonal terrain":"polygonal terrain", "residual ice":"residual ice", "patterned ground (type 1)":"patterned ground (type 1)", "patterend ground (type 2)":"patterend ground (type 2)", "generic overlay 1":"generic overlay 1", "generic overlay 2":"generic overlay 2"}
+        zonesDict = {"dike 1.3.13":"dike of variable thickness", "shear zone 2.14.2":"shear zone within fault", "breccia zone 2.14.3":"breccia zone within fault", "breccia zone 2.14.4":"breccia zone around fault", "slip surface 17.9":"area of slip surface of landslide", "debris-slide slope 17.65":"debris-slide slope (multiple)"}
+
+        #Add coded values to domains
+        for code in contactDict:
+            arcpy.AddCodedValueToDomain_management(GDBpath, "contact", code, contactDict[code])
+        for code in contactsdikesDict:
+            arcpy.AddCodedValueToDomain_management(GDBpath, "contacts, key beds, and dikes", code, contactsdikesDict[code])
+        for code in eolianDict:
+            arcpy.AddCodedValueToDomain_management(GDBpath, "eolian features", code, eolianDict[code])
+        for code in faultsDict:
+            arcpy.AddCodedValueToDomain_management(GDBpath, "faults", code, faultsDict[code])
+        for code in foldsDict:
+            arcpy.AddCodedValueToDomain_management(GDBpath, "folds", code, foldsDict[code])
+        for code in landslideDict:
+            arcpy.AddCodedValueToDomain_management(GDBpath, "landslide and mass-wasting", code, landslideDict[code])
+        for code in lineamentDict:
+            arcpy.AddCodedValueToDomain_management(GDBpath, "lineaments and joints", code, lineamentDict[code])
+        for code in linearDict:
+            arcpy.AddCodedValueToDomain_management(GDBpath, "linear feature", code, linearDict[code])
+        for code in pointDict:
+            arcpy.AddCodedValueToDomain_management(GDBpath, "point feature", code, pointDict[code])
+        for code in polygonDict:
+            arcpy.AddCodedValueToDomain_management(GDBpath, "polygon feature", code, polygonDict[code])
+        for code in zonesDict:
+            arcpy.AddCodedValueToDomain_management(GDBpath, "zones and blocks", code, zonesDict[code])
+
+        #Create feature dataset and feature classes
+        arcpy.CreateFeatureDataset_management(GDBpath, "domain_proj_clon", boundarySpatialRef)
+        FDpath = GDBpath + "/" + "domain_proj_clon"
+        arcpy.CreateFeatureclass_management(FDpath, "GeoContacts", "POLYLINE")
+        arcpy.CreateFeatureclass_management(FDpath, "LinearFeatures", "POLYLINE")
+        arcpy.CreateFeatureclass_management(FDpath, "LocationFeatures", "POINT")
+        arcpy.CreateFeatureclass_management(FDpath, "MapBoundary", "POLYGON")
+        arcpy.CreateFeatureclass_management(FDpath, "SurfaceFeatures", "POLYGON")
+
+        #Add fields with coded domains
+        contactsFC = FDpath + "/" + "GeoContacts"
+        linearFC = FDpath + "/" + "LinearFeatures"
+        locationFC = FDpath + "/" + "LocationFeatures"
+        boundaryFC = FDpath + "/" + "MapBoundary"
+        surfaceFC = FDpath + "/" + "SurfaceFeatures"
+
+        arcpy.AddField_management(contactsFC, "TYPE", "TEXT", "", "", 50, "TYPE", "NULLABLE", "REQUIRED", "contact")
+        arcpy.AddField_management(contactsFC, "COMMENT", "TEXT", "", "", 250, "COMMENT", "NULLABLE", "REQUIRED", "")
+        arcpy.AddField_management(linearFC, "TYPE", "TEXT", "", "", 50, "TYPE", "NULLABLE", "REQUIRED", "linear feature")
+        arcpy.AddField_management(linearFC, "COMMENT", "TEXT", "", "", 250, "COMMENT", "NULLABLE", "REQUIRED", "")
+        arcpy.AddField_management(locationFC, "TYPE", "TEXT", "", "", 50, "TYPE", "NULLABLE", "REQUIRED", "point feature")
+        arcpy.AddField_management(locationFC, "COMMENT", "TEXT", "", "", 250, "COMMENT", "NULLABLE", "REQUIRED", "")
+        arcpy.AddField_management(surfaceFC, "TYPE", "TEXT", "", "", 50, "TYPE", "NULLABLE", "REQUIRED", "polygon feature")
+        arcpy.AddField_management(surfaceFC, "COMMENT", "TEXT", "", "", 250, "COMMENT", "NULLABLE", "REQUIRED", "")
+        arcpy.AddField_management(boundaryFC, "QUAD_NAME", "TEXT", "", "", 250, "QUAD_NAME", "NULLABLE", "REQUIRED", "")
+        arcpy.AddField_management(boundaryFC, "LAT_MIN", "FLOAT", "", "", "", "", "NULLABLE", "REQUIRED", "")
+        arcpy.AddField_management(boundaryFC, "LAT_MAX", "FLOAT", "", "", "", "", "NULLABLE", "REQUIRED", "")
+        arcpy.AddField_management(boundaryFC, "LON_MIN", "FLOAT", "", "", "", "", "NULLABLE", "REQUIRED", "")
+        arcpy.AddField_management(boundaryFC, "LON_MAX", "FLOAT", "", "", "", "", "NULLABLE", "REQUIRED", "")
+        arcpy.AddField_management(boundaryFC, "CLON", "FLOAT", "", "", "", "", "NULLABLE", "REQUIRED", "")
+        arcpy.AddField_management(boundaryFC, "SP_1", "FLOAT", "", "", "", "", "NULLABLE", "REQUIRED", "")
+        arcpy.AddField_management(boundaryFC, "SP_2", "FLOAT", "", "", "", "", "NULLABLE", "REQUIRED", "")
+
+        #Import map boundary to MapBoundary feature class and GeoContacts
+        arcpy.Append_management(boundaryFullPath, boundaryFC, "NO_TEST")
+        boundaryLine = "in_memory/boundaryLine"
+        arcpy.PolygonToLine_management(boundaryFullPath, boundaryLine)
+        arcpy.Append_management(boundaryLine, contactsFC, "NO_TEST")
+        arcpy.CalculateField_management(contactsFC, "TYPE", '"boundary"')
+        arcpy.Delete_management(boundaryLine)
+        return
+
+class MergeFiles(object):
+    def __init__(self):
+        """Define the tool (tool name is the name of the class)."""
+        self.label = "Merge Files"
+        self.description = ""
+        self.canRunInBackground = False
+
+    def getParameterInfo(self):
+        """Define parameter definitions"""
+        param0 = arcpy.Parameter(
+            displayName="Workspace",
+            name="workspace",
+            datatype=["DEWorkspace", "DEFeatureDataset"],
+            parameterType="Required",
+            direction="Input")
+        param1 = arcpy.Parameter(
+            displayName="Wildcard",
+            name="wildcard",
+            datatype="GPString",
+            parameterType="Optional",
+            direction="Input")
+        param2 = arcpy.Parameter(
+            displayName="Feature Type",
+            name="featureType",
+            datatype="GPString",
+            parameterType="Required",
+            direction="Input")
+        param3 = arcpy.Parameter(
+            displayName="Recursive",
+            name="recursive",
+            datatype="GPBoolean",
+            parameterType="Optional",
+            direction="Input")
+        param4 = arcpy.Parameter(
+            displayName="Output Feature Class",
+            name="outFC",
+            datatype="DEFeatureClass",
+            parameterType="Required",
+            direction="Output")
+        parameters = [param0, param1, param2, param3, param4]
+        param2.filter.list = ["POLYGON", "POLYLINE", "POINT", "MULTIPOINT"]
+        return parameters
+
+    def isLicensed(self):
+        """Set whether tool is licensed to execute."""
+        return True
+
+    def updateParameters(self, parameters):
+        """Modify the values and properties of parameters before internal
+        validation is performed.  This method is called whenever a parameter
+        has been changed."""
+        return
+
+    def updateMessages(self, parameters):
+        """Modify the messages created by internal validation for each tool
+        parameter.  This method is called after internal validation."""
+        return
+
+    def execute(self, parameters, messages):
+        """The source code of the tool."""
+# Script arguments
+
+        def recursive_list_fcs(workspace, wild_card=None, feature_type=None):
+            """Returns a list of all feature classes in a tree.  Returned
+            list can be limited by a wildcard, and feature type.
+            """
+            preexisting_wks = arcpy.env.workspace
+            arcpy.env.workspace = workspace
+            try:
+                list_fcs = []
+                for root, dirs, files in os.walk(workspace):
+                    arcpy.env.workspace = root
+                    fcs = arcpy.ListFeatureClasses(wild_card, feature_type)
+                    if fcs:
+                        list_fcs += [os.path.join(root, fc) for fc in fcs]
+            except Exception as err:
+                raise err
+            finally:
+                arcpy.env.workspace = preexisting_wks
+            return list_fcs
+        
+        Workspace = parameters[0].valueAsText
+        Wildcard = parameters[1].valueAsText
+        FeatureType = parameters[2].valueAsText
+        Recursive = parameters[3].valueAsText
+        outputFC = parameters[4].valueAsText
+        
+        if Recursive == '#' or not Recursive:
+            Recursive = "false" # provide a default value if unspecified
+
+# Local variables:
+        FieldName = "Filename"
+        outputFCpath, outputFCname = os.path.split(outputFC)
+
+# Process: Iterate Feature Classes
+        arcpy.env.workspace = Workspace
+        if Recursive == "true":
+            WorkspaceList = recursive_list_fcs(Workspace, Wildcard, FeatureType)
+        elif Recursive == "false":
+            if Wildcard:
+                WorkspaceList = arcpy.ListFeatureClasses(Wildcard, FeatureType)
+            else:
+                WorkspaceList = arcpy.ListFeatureClasses("", FeatureType, "")
+        
+        arcpy.AddMessage(WorkspaceList)
+        s_list = [] #list of inputs for arcpy.Merge
+        for s in WorkspaceList:
+            s_list.append(s)
+            sDesc = arcpy.Describe(s)
+            sName = sDesc.name
+            if sName.endswith('.shp'):
+                sName = sName[:-4]
+            arcpy.AddMessage(sName)
+            arcpy.AddField_management(s, FieldName, "TEXT", "", "", "124", "", "NULLABLE", "NON_REQUIRED", "")
+            expression = """'{}'""".format(sName)
+            arcpy.CalculateField_management(s, FieldName, expression, "PYTHON_9.3")
+            #Can't use append due to fields with spaces. Try merge instead.
+            #arcpy.Append_management(s, outputFC, "NO_TEST", "", "")
+            
+        arcpy.Merge_management(s_list, outputFC, "")
+        return
+
+class TopologyCheck(object):
+    def __init__(self):
+        """Define the tool (tool name is the name of the class)."""
+        self.label = "Topology Check"
+        self.description = ""
+        self.canRunInBackground = False
+
+    def getParameterInfo(self):
+        """Define parameter definitions"""
+        param0 = arcpy.Parameter(
+            displayName="Input Feature Class",
+            name="in_fc",
+            datatype="GPFeatureLayer",
+            parameterType="Required",
+            direction="Input")
+        param1 = arcpy.Parameter(
+            displayName="Cluster Tolerance",
+            name="cluster_tolerance",
+            datatype="GPString",
+            parameterType="Optional",
+            direction="Input")
+        parameters = [param0, param1]
+        return parameters
+
+    def isLicensed(self):
+        """Set whether tool is licensed to execute."""
+        return True
+
+    def updateParameters(self, parameters):
+        """Modify the values and properties of parameters before internal
+        validation is performed.  This method is called whenever a parameter
+        has been changed."""
+        return
+
+    def updateMessages(self, parameters):
+        """Modify the messages created by internal validation for each tool
+        parameter.  This method is called after internal validation."""
+        return
+
+    def execute(self, parameters, messages):
+        inputFC = parameters[0].valueAsText
+        clusterToleranceIn = parameters[1].valueAsText
+        clusterTolerance = ""
+        if clusterToleranceIn:
+            clusterTolerance = float(clusterToleranceIn)
+        inputFCdesc = arcpy.Describe(inputFC)
+        inFCshape = inputFCdesc.shapeType
+        inputFCpath = inputFCdesc.path
+        inputFCname = inputFCdesc.name
+        inputFCfullpath = inputFCdesc.catalogPath
+        outputTopologyName = inputFCname + "_Topology"
+        outputTopology = inputFCpath + "\\" + outputTopologyName
+#Create topology and add FC
+        arcpy.CreateTopology_management(inputFCpath, outputTopologyName, clusterTolerance)
+        arcpy.AddFeatureClassToTopology_management(outputTopology, inputFC, 1, 1)
+#Add rules based on geometry type
+        if inFCshape == "Polygon":
+            arcpy.AddRuleToTopology_management(outputTopology, "Must Not Have Gaps (Area)", inputFCfullpath)
+            arcpy.AddRuleToTopology_management(outputTopology, "Must Not Overlap (Area)", inputFCfullpath)
+        elif inFCshape == "Polyline":
+            arcpy.AddRuleToTopology_management(outputTopology, "Must Not Have Dangles (Line)", inputFCfullpath)
+            arcpy.AddRuleToTopology_management(outputTopology, "Must Not Have Pseudo-Nodes (Line)", inputFCfullpath)
+            arcpy.AddRuleToTopology_management(outputTopology, "Must Not Self-Overlap (Line)", inputFCfullpath)
+            arcpy.AddRuleToTopology_management(outputTopology, "Must Not Intersect (Line)", inputFCfullpath)
+            arcpy.AddRuleToTopology_management(outputTopology, "Must Not Overlap (Line)", inputFCfullpath)
+            arcpy.AddRuleToTopology_management(outputTopology, "Must Not Self-Intersect (Line)", inputFCfullpath)
+        else:
+            arcpy.ErrorMessage("This tool only support polygon and polyline geometry types")
+            return
+#Validate topology and export error report
+        arcpy.ValidateTopology_management(outputTopology)
+        arcpy.ExportTopologyErrors_management(outputTopology, inputFCpath, inputFCname)
+#Collate results and delete intermediate tables
+        
+        return
+
+class BuildPolygons(object):
+    def __init__(self):
+        """Define the tool (tool name is the name of the class)."""
+        self.label = "Build Polygons"
+        self.description = ""
+        self.canRunInBackground = False
+
+    def getParameterInfo(self):
+        """Define parameter definitions"""
+        param0 = arcpy.Parameter(
+            displayName="Input Line Feature Class",
+            name="in_linefc",
+            datatype="GPFeatureLayer",
+            parameterType="Required",
+            direction="Input")
+        param1 = arcpy.Parameter(
+            displayName="Input Polygon Feature Class",
+            name="in_polygonfc",
+            datatype="GPFeatureLayer",
+            parameterType="Optional",
+            direction="Input")
+        param2 = arcpy.Parameter(
+            displayName="Output Feature Class",
+            name="out_fc",
+            datatype="DEFeatureClass",
+            parameterType="Required",
+            direction="Output")
+        param0.filter.list = ["Polyline"]
+        parameters = [param0, param1, param2]
+        return parameters
+
+    def isLicensed(self):
+        """Set whether tool is licensed to execute."""
+        return True
+
+    def updateParameters(self, parameters):
+        """Modify the values and properties of parameters before internal
+        validation is performed.  This method is called whenever a parameter
+        has been changed."""
+        return
+
+    def updateMessages(self, parameters):
+        """Modify the messages created by internal validation for each tool
+        parameter.  This method is called after internal validation."""
+        return
+
+    def execute(self, parameters, messages):
+#Read in parameters
+        inputLineFCstr = parameters[0].valueAsText
+        inputPolygonFCstr = parameters[1].valueAsText
+        outputFC = parameters[2].valueAsText
+        
+        inputLineFCdesc = arcpy.Describe(inputLineFCstr)
+        lineFC = inputLineFCdesc.catalogPath
+
+#Execute feature to polygon based on optional parameter
+        if inputPolygonFCstr:
+            inputPolygonFCdesc = arcpy.Describe(inputPolygonFCstr)
+            polygonFC= inputPolygonFCdesc.catalogPath
+            arcpy.Delete_management("in_memory")
+            tempPoints = "in_memory\\tempPoints"
+            arcpy.FeatureToPoint_management(polygonFC, tempPoints, "INSIDE")
+            arcpy.FeatureToPolygon_management(lineFC, outputFC, "", "", tempPoints)
+            arcpy.Delete_management(tempPoints)
+        else:
+            arcpy.FeatureToPolygon_management(lineFC, outputFC)
+            arcpy.AddField_management(outputFC, "Type", "TEXT")
+        return
+
+
+class SlopeAspectTest(object):
+    def __init__(self):
+        """Define the tool (tool name is the name of the class)."""
+        self.label = "Slope & Aspect Test"
+        self.description = ""
+        self.canRunInBackground = False
+
+    def getParameterInfo(self):
+        """Define parameter definitions"""
+        param0 = arcpy.Parameter(
+            displayName="Input Point Feature Class",
+            name="in_pointfc",
+            datatype="GPFeatureLayer",
+            parameterType="Required",
+            direction="Input")
+        param1 = arcpy.Parameter(
+            displayName="Field Name",
+            name="in_field",
+            datatype="Field",
+            parameterType="Required",
+            direction="Input")
+        param2 = arcpy.Parameter(
+            displayName="Input DEM",
+            name="in_dem",
+            datatype="GPRasterLayer",
+            parameterType="Required",
+            direction="Input")
+##        param3 = arcpy.Parameter(
+##            displayName="Tolerance (Degrees)",
+##            name="in_tolerance",
+##            datatype="GPDouble",
+##            parameterType="Required",
+##            direction="Input")
+        param3 = arcpy.Parameter(
+            displayName="Test Type",
+            name="in_test",
+            datatype="GPString",
+            parameterType="Required",
+            direction="Input")
+        param4 = arcpy.Parameter(
+            displayName="Output Feature Class",
+            name="out_fc",
+            datatype="DEFeatureClass",
+            parameterType="Required",
+            direction="Output")
+        param0.filter.list = ["Point"]
+        param3.filter.type = "ValueList"
+        param3.filter.list = ["Slope", "Aspect"]
+        param1.parameterDependencies = [param0.name]
+        parameters = [param0, param1, param2, param3, param4]
+        return parameters
+
+    def isLicensed(self):
+        """Set whether tool is licensed to execute."""
+        return True
+
+    def updateParameters(self, parameters):
+        """Modify the values and properties of parameters before internal
+        validation is performed.  This method is called whenever a parameter
+        has been changed."""
+        return
+
+    def updateMessages(self, parameters):
+        """Modify the messages created by internal validation for each tool
+        parameter.  This method is called after internal validation."""
+        return
+
+    def execute(self, parameters, messages):
+#read in parameters
+        inputPointFCstr = parameters[0].value
+        fieldName = parameters[1].valueAsText
+        inputDEMlayer = parameters[2].value
+#        tolerance = float(parameters[3].valueAsText)
+        testType = parameters[3].valueAsText
+        outPoints = parameters[4].valueAsText
+
+#local variables
+        DEMdesc = arcpy.Describe(inputDEMlayer)
+        inDEM = DEMdesc.catalogPath
+        DEMname = DEMdesc.name
+        pointDesc = arcpy.Describe(inputPointFCstr)
+        pointFC = pointDesc.catalogPath
+        arcpy.env.workspace = DEMdesc.path
+
+#DEM processing based on test type
+        if testType == "Aspect":
+            DEMtemp = Aspect(inDEM)
+        elif testType == "Slope":
+            DEMtemp = Slope(inDEM, "DEGREE")
+
+#Extract values of processed DEM to points
+        ExtractValuesToPoints(pointFC, DEMtemp, outPoints, "INTERPOLATE", "VALUE_ONLY")
+        newField = testType + "_PyGM"
+        arcpy.AlterField_management(outPoints, "RASTERVALU", newField)
+
+#Create feature layer and select records outside tolerance
+##        outPointsDesc = arcpy.Describe(outPoints)
+##        outPointsName = outPointsDesc.name
+##        outPointsLayer = outPointsName + "_Layer"
+##        arcpy.MakeFeatureLayer_management(outPoints, outPointsLayer)
+##        mxd = arcpy.mapping.MapDocument("CURRENT")
+##        dataFrame = arcpy.mapping.ListDataFrames(mxd, "*")[0] 
+##        addLayer = arcpy.mapping.Layer(outPointsLayer)
+##        arcpy.mapping.AddLayer(dataFrame, addLayer)
+##        arcpy.RefreshActiveView()
+##        pointCursor = arcpy.SearchCursor(outPointsLayer)
+##        for row in pointCursor:
+##            pointValue = row.getValue(fieldName)
+##            DEMvalue = float(row.getValue(newField))
+##            delimitedFieldName = arcpy.AddFieldDelimiters(outPointsLayer, fieldName)
+##            whereClause = delimitedFieldName + " > " + str(DEMvalue + tolerance) + " OR " + delimitedFieldName + " < " + str(DEMvalue - tolerance)
+##            arcpy.AddMessage(whereClause)
+##            arcpy.SelectLayerByAttribute_management(outPointsLayer, "ADD_TO_SELECTION", whereClause)
+        try:
+            arcpy.RefreshActiveView()
+        except:
+            Pass
+#Delete slope/aspect raster
+        if arcpy.Exists(DEMtemp):
+            arcpy.Delete_management(DEMtemp)
+        
+        return