Select to view content in your preferred language

Plot shapefiles from gdb to an aprx file

134
1
06-07-2024 10:11 AM
HusamOqer
New Contributor II

Hello,

 

I am working on a scrip that create 2 aprx files ( by copying an existing one then change their names) and after that plot the shapefiles from 2 different gdb's to the corresponding aprx file and after that apply the symbology.

 

Is there a way I can do that in one script (running the code in Pyscripter not within the ArcGIS pro)

The scrip I created can copy the features to the gdb's, change their names and copy and rename the aprx files but when I open the aprx file it's empty with no layers inside it.

 

Note: When I run it in Arc GIS Pro it show the shapefiles and copy the symbology but in this case it do it in a different aprx file not the targeted one.

import arcpy
import os
import shutil

# Enable overwriting
arcpy.env.overwriteOutput = True

# Define the source folder and area name
source_folder = r'C:\Users\HusamOqer\Desktop\2D 3D script\PROJECTAREA37\FinalDelivery' # Update this with your source folder path
Area_Name = "Dorset_"

empty_project_path = os.path.join(source_folder, 'Symbology', 'Project.aprx')

# Function to copy and rename the project file
def copy_and_rename_project(original_path, new_folder, new_name):
new_path = os.path.join(new_folder, f'{new_name}.aprx')
shutil.copyfile(original_path, new_path)
return new_path

# Copy and rename the project file for 2D
aprx_2d_path = copy_and_rename_project(empty_project_path, os.path.join(source_folder, 'Geodatabase_2D'), Area_Name + '2D')

# Copy and rename the project file for 3D
aprx_3d_path = copy_and_rename_project(empty_project_path, os.path.join(source_folder, 'Geodatabase_3D'), Area_Name + '3D')

# Define paths
folder_2d_path = os.path.join(source_folder, 'SHPrenamed_2D')
folder_3d_path = os.path.join(source_folder, 'SHPrenamed_3D')
destination_gdb_2D = os.path.join(source_folder, 'Geodatabase_2D', Area_Name + '2D.gdb')
destination_gdb_3D = os.path.join(source_folder, 'Geodatabase_3D', Area_Name + '3D.gdb')
Symbology = os.path.join(source_folder, "Symbology")

# Function to import shapefiles into the specified geodatabase
def import_shapefiles(source, destination_gdb):
arcpy.env.workspace = destination_gdb

# Ensure the destination geodatabase exists
if not arcpy.Exists(destination_gdb):
arcpy.CreateFileGDB_management(os.path.dirname(destination_gdb), os.path.basename(destination_gdb))

for root, dirs, files in os.walk(source):
for file in files:
if file.lower().endswith('.shp'):
source_shapefile = os.path.join(root, file)
feature_class_name = os.path.splitext(file)[0]

# Ensure the output feature class does not already exist
output_feature_class = os.path.join(destination_gdb, feature_class_name)
if not arcpy.Exists(output_feature_class):
arcpy.FeatureClassToGeodatabase_conversion(source_shapefile, destination_gdb)
print(f"Imported {file} into {destination_gdb}/{feature_class_name}")
else:
print(f"Skipped {file} - Feature class already exists in {destination_gdb}")

# Import shapefiles from SHPrenamed_3D to Geodatabase_3D
import_shapefiles(folder_3d_path, destination_gdb_3D)

# Import shapefiles from SHPrenamed_2D to Geodatabase_2D
import_shapefiles(folder_2d_path, destination_gdb_2D)

# Rename the 3D geodatabase
new_destination_gdb_3D = os.path.join(os.path.dirname(destination_gdb_3D), Area_Name + '3D.gdb')
if not arcpy.Exists(new_destination_gdb_3D):
arcpy.management.Rename(destination_gdb_3D, new_destination_gdb_3D)
else:
print(f"The destination geodatabase {new_destination_gdb_3D} already exists. Skipping rename.")

# Rename the 2D geodatabase
new_destination_gdb_2D = os.path.join(os.path.dirname(destination_gdb_2D), Area_Name + '2D.gdb')
if not arcpy.Exists(new_destination_gdb_2D):
arcpy.management.Rename(destination_gdb_2D, new_destination_gdb_2D)
else:
print(f"The destination geodatabase {new_destination_gdb_2D} already exists. Skipping rename.")

# Function to add layers from a geodatabase to a map
def add_layers_to_map(aprx_path, gdb_path, symbology_folder, suffix):
aprx = arcpy.mp.ArcGISProject(aprx_path)
map_obj = aprx.listMaps()[0]

# Loop through the feature classes in the geodatabase
for dirpath, dirnames, filenames in arcpy.da.Walk(gdb_path, datatype="FeatureClass"):
for filename in filenames:
layerfile = os.path.join(dirpath, filename)
if filename == "PoleIDAnno":
temp_layer = "PoleIDAnno"
arcpy.management.MakeFeatureLayer(layerfile, temp_layer)
addlayer = os.path.abspath(temp_layer)
else:
addlayer = os.path.abspath(layerfile)

map_obj.addDataFromPath(addlayer)

# Loop through the .lyr and .lyrx files in the symbology folder
for file in os.listdir(symbology_folder):
if file.endswith((".lyr", ".lyrx")):
lyr_path = os.path.join(symbology_folder, file)
lyr_name = os.path.splitext(file)[0]

# Check if the .lyr or .lyrx file name matches any layer name in the map
for lyr in map_obj.listLayers():
if lyr_name in lyr.name and lyr.name.endswith(suffix): # Apply symbology only if the layer ends with suffix
print(f"Applying symbology from {lyr_path} to layer {lyr.name}")
arcpy.management.ApplySymbologyFromLayer(lyr, lyr_path)
print("Symbology applied!")
break # Exit loop after finding a match

# Save changes to the ArcGIS Pro project
aprx.save()

# Add layers and apply symbology to the 2D project
add_layers_to_map(aprx_2d_path, new_destination_gdb_2D, Symbology, '_2D')

# Add layers and apply symbology to the 3D project
add_layers_to_map(aprx_3d_path, new_destination_gdb_3D, Symbology, '_3D')

# Cleanup unnecessary geodatabases
def cleanup_geodatabases(source_folder):
for folder_name in ['Geodatabase_2D', 'Geodatabase_3D']:
full_folder_path = os.path.join(source_folder, folder_name)

for gdb_name in os.listdir(full_folder_path):
gdb_path = os.path.join(full_folder_path, gdb_name)

# Check if the gdb_name starts with the correct Area_Name
if gdb_name.startswith(Area_Name):
print(f"Keeping geodatabase: {gdb_path}")
else:
print(f"Deleting unnecessary geodatabase: {gdb_path}")
arcpy.management.Delete(gdb_path)

# Run cleanup
cleanup_geodatabases(source_folder)

1 Reply
HaydenWelch
Occasional Contributor II
import arcpy
import os
import shutil

## Folder Structure:
# Source
# ├── Project_2D.aprx
# ├── Project_3D.aprx
# ├── Databases
# │   ├── db_2D.gdb
# │   └── db_3D.gdb
# ├── Layers
# │   ├── layer_2D.lyrx
# │   └── layer_3D.lyrx
# └── Shapefiles
#     ├── shape_2D.shp
#     └── shape_3D.shp

# Target
# ├── db_2D.gdb
# ├── db_3D.gdb
# ├── Project_2D.aprx
# └── Project_3D.aprx

    
class Builder:
    def __init__(self, source: os.PathLike, suffixes: list[str]= ["2D", "3D"]):
        self.suffixes = suffixes
        self._projects = \
            {
                prj.split('.')[0]: arcpy.mp.ArcGISProject(os.path.join(source, prj))
                for prj in os.listdir(source)
                if prj.endswith(".aprx")
            }
        self._shapefiles = \
            {
                sf.split('.')[0]: os.path.join(source, "Shapefiles", sf)
                for sf in os.listdir(os.path.join(source, "Shapefiles"))
                if sf.endswith(".shp")
            }
        self._layers = \
            {
                lyr.split('.')[0]: os.path.join(source, "Layers", lyr)
                for lyr in os.listdir(os.path.join(source, "Layers"))
                if lyr.endswith(".lyrx")
            }
        self._databases = \
            {
                db.split('.')[0]: os.path.join(source, "Databases", db)
                for db in os.listdir(os.path.join(source, "Databases"))
                if db.endswith(".gdb")
            }
        return
    
    def create(self, target: os.PathLike):
        """ Create a new project from the source folder 
        target: str, path to the target folder
        """
        for suffix in self.suffixes:
            print(f"Creating {suffix} project")
            # create/get target project
            project = self._get_new_project(target, suffix)
            print(f"Creating {suffix} database")
            # create/get target database
            target_db = self._get_target_db(target, suffix)
            project.updateDatabases(
                [
                    {
                        'databasePath': target_db,
                        'isDefaultDatabase': True,
                    },
                ]
            )
            # Copy shapefiles into gdb and create layers
            print(f"Creating {suffix} layers")
            self._build_layer(suffix, project, target_db)
            project.save()
        return

    def _build_layer(self, suffix, project, target_db):
        for sf_name, sf in self._shapefiles.items():
            if not sf_name.endswith(suffix): continue
            # Create New Feature Class
            new_feature = arcpy.conversion.FeatureClassToFeatureClass(sf, target_db, sf_name)
            # Get Matching LYRX file
            new_feature_symbology = self._layers[sf_name]
            # Get first map and add data
            new_layer = project.listMaps()[0].addDataFromPath(new_feature)
            # Apply Symbology
            arcpy.management.ApplySymbologyFromLayer(new_layer, new_feature_symbology)
        return
    
    def _get_target_db(self, target, suffix):
        for db_name, db in self._databases.items():
            if not db_name.endswith(suffix): continue
            return shutil.copytree(db, os.path.join(target, f"{db_name}.gdb"))
        raise ValueError(f"No database for {suffix} found in source folder")

    def _get_new_project(self, target, suffix):
        for prj_name, prj in self._projects.items():
            if not prj_name.endswith(suffix): continue
            new_project_path = os.path.join(target, f"{prj_name}.aprx")
            # Copy Project and Set copy to active project
            shutil.copy(prj.filePath, new_project_path)
            return arcpy.mp.ArcGISProject(new_project_path)
        raise ValueError(f"No project for {suffix} found in source folder")
    
def main():
    source = r"<source>"
    target = r"<target>"
    builder = Builder(source, suffixes=['2D', '3D'])
    builder.create(target)
    return

def main2():
    source = r"<source>"
    targets = [r"<target1>", r"<target2>", r"<target3>", r"<target4>"]
    builder = Builder(source)
    for target in targets:
        builder.create(target)
    return

if __name__ == "__main__":
    main()
    #main2()

 

I was having a hard time following your program flow, but I attempted to re-implement it in a slightly more modular way. Now all you need to do is add methods to the builder object ten call them in order in that create function to add steps. The __init__ function relies on the Source folder to be structured as shown in the comment at the top of the code, but changing those `for <object> in os.path.join(<root>, <subfolder>, <arcobject>)` to something else should be easy.

0 Kudos