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)
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.