|
BLOG
|
@KoryKramer Great! Thanks for including that. Wish I'd found it sooner, but sometimes it comes down to the terms you use when searching!
... View more
02-12-2025
07:29 PM
|
1
|
0
|
904
|
|
BLOG
|
With crunch time coming and ArcMap set to become a thing of the past for many GIS users, even after migrating to ArcGIS Pro for the majority of works, there are still cases where ArcMap has been held on to as a way of accessing datasets that were stored in Personal geodatabases (.mdb). However, once ArcMap is gone, there will be no way to view this data using ArcGIS Pro. If you only have a few .mdb's, this isn't a big deal as you can manually create a new .gdb and migrate your datasets across using the tools already at your disposal in ArcMap. But....if you have many .mdb's, this soon becomes unfeasible. We fell into this category where we had many archived and backup datasets stored as .mdb's that we may want to access in the future. With ArcMap planned to be uninstalled from all machines across our organisation later this year, we decided it was time to convert these to file geodatabases before that happened to guarantee future accessibility. To achieve this, I used ArcMap's python window with 2 sets of 2 scripts as detailed below. NOTE: When running the below scripts to update data on our network, they were very slow to perform almost every task. Moving the data (make a copy!) to the local C drive of my machine sped up the processing time immensely (Many many hours down to minutes!). It also reduced the number of failed copy tasks to almost 0. Script 1: This script would be copy and pasted into ArcMaps python window. Update the input_folder parameter with the parent folder containing personal geodatabases. This will also loop through all subfolders and their contained .mdb's. The loop passes the .mdb pathway to the second script. Update the second_script_path parameter with the pathway to the second script. Script 2: Called by script 1, this script is saved in a location of your choosing. It undertakes the creation of a matching .gdb and copies across any datasets, feature classes or tables found in the input .mdb. The process was broken out into 2 scripts instead of being run in a single instance (script) because I found that after the first folder in the loop, all .mdb's found in subsequent folders would fail to have their data copied, returning many error messages. The second script is executed per .mdb found and this resets all the inputs, removing whatever issue caused it to fail when looped in the same instance. Script 1 import os
import subprocess
from datetime import datetime
def iterate_folders(input_folder, second_script_path):
# Create a log file
log_file = os.path.join(r"C:\temp", "MDB_to_GDB_log_{}.txt".format(datetime.now().strftime('%Y%m%d_%H%M%S')))
def log_message(message):
print(message)
with open(log_file, 'a') as log:
log.write(message + '\n')
# Iterate through all subfolders and files
for root, dirs, files in os.walk(input_folder):
for file in files:
# Check if the file is a .mdb file
if file.endswith(".mdb"):
mdb_path = os.path.join(root, file)
log_message("Processing MDB: " + mdb_path)
# Call the second script to create GDB and copy items
subprocess.call(["python", second_script_path, root, log_file])
# Example usage
input_folder = r"C:\temp\FoldersWithMDBs"
second_script_path = r"C:\Temp\Script2.py" # Specify the full path to the second script
if not os.path.exists(input_folder):
print("The path " + input_folder + " does not exist.")
else:
iterate_folders(input_folder, second_script_path) Script 2 import os
import sys
import arcpy
from datetime import datetime
def create_gdb_and_copy_items(folder_path, log_file):
def log_message(message):
print(message)
with open(log_file, 'a') as log:
log.write(message + '\n')
# Iterate through all files in the folder
for file in os.listdir(folder_path):
# Check if the file is a .mdb file
if file.endswith(".mdb"):
mdb_path = os.path.join(folder_path, file)
gdb_name = os.path.splitext(file)[0] + ".gdb"
gdb_path = os.path.join(folder_path, gdb_name)
log_message("Processing MDB: " + mdb_path)
# Check if the GDB already exists
if not arcpy.Exists(gdb_path):
try:
# Create a new file geodatabase using arcpy
arcpy.management.CreateFileGDB(folder_path, gdb_name)
except Exception as e:
log_message("Error creating GDB {}: {}".format(gdb_path, str(e)))
continue
else:
log_message("GDB already exists: " + gdb_path)
try:
# List all datasets in the .mdb
arcpy.env.workspace = mdb_path
datasets = arcpy.ListDatasets("*", "All")
log_message("Datasets: " + str(datasets))
except Exception as e:
log_message("Error listing datasets in {}: {}".format(mdb_path, str(e)))
datasets = []
try:
# Copy each dataset to the new .gdb
for dataset in datasets:
if not arcpy.Exists(os.path.join(gdb_path, dataset)):
arcpy.management.Copy(dataset, os.path.join(gdb_path, dataset))
log_message(str(dataset) + " copied")
else:
log_message(str(dataset) + " already exists in GDB")
except Exception as e:
log_message("Error copying dataset {}: {}".format(dataset, str(e)))
continue
try:
# List all feature classes in the .mdb
arcpy.env.workspace = mdb_path
fcs = arcpy.ListFeatureClasses("*", "All")
log_message("Feature classes: " + str(fcs))
except Exception as e:
log_message("Error listing feature classes in {}: {}".format(mdb_path, str(e)))
fcs = []
try:
# Copy each feature class to the new .gdb
for fc in fcs:
if not arcpy.Exists(os.path.join(gdb_path, fc)):
arcpy.management.Copy(fc, os.path.join(gdb_path, fc))
log_message(str(fc) + " copied")
else:
log_message(str(fc) + " already exists in GDB")
except Exception as e:
log_message("Error copying feature class {}: {}".format(fc, str(e)))
continue
try:
# List all tables in the .mdb
arcpy.env.workspace = mdb_path
tables = arcpy.ListTables("*", "All")
log_message("Tables: " + str(tables))
except Exception as e:
log_message("Error listing tables in {}: {}".format(mdb_path, str(e)))
tables = []
try:
# Copy each table to the new .gdb, excluding those starting with 'dbo_'
for table in tables:
if not table.startswith("dbo_"):
if not arcpy.Exists(os.path.join(gdb_path, table)):
arcpy.management.Copy(table, os.path.join(gdb_path, table))
log_message(str(table) + " copied")
else:
log_message(str(table) + " already exists in GDB")
else:
log_message("Skipping table: " + str(table))
except Exception as e:
log_message("Error copying table {}: {}".format(table, str(e)))
continue
log_message("Converted " + mdb_path + " to " + gdb_path)
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: python create_gdb_and_copy_items.py <folder_path> <log_file>")
else:
folder_path = sys.argv[1]
log_file = sys.argv[2]
create_gdb_and_copy_items(folder_path, log_file) Script 2 notes: You may have more than just datasets, feature classes and tables to migrate and thus may need to customise the script to suit your needs. Also, I've had to filter out tables starting with dbo_ as these were connections to long lost databases and caused the script to get stuck trying to access those databases. The next 2 scripts were intended mainly as a double checking process. Once the first scripts had run, occasionally I would see error messages where a feature class or table failed to copy. This would be caught and logged but the script would continue on. Reviewing the log file helped to identify how many of these failures occurred. One or 2 and you may choose to fix manually. Script 3: Very similar to Script 1 in that it loops through folders and personal geodatabases. The difference is here is that it would generate the path for .gdb's that should now exist from the located .mdb and would pass this to Script 4 as well. It is executed the same way (by pasting into ArcMaps python window and calls Script 4 using the referenced pathway) Script 4: This process lists all datasets/feature classes/tables in the input .mdb AND .gdb for comparison. If the lists match, nothing happens, but if they don't match, it attempts to copy the missing items across to complete the list. NOTE: It has the same filter as Script to exclude "dbo_" tables. Script 3 import os
import subprocess
from datetime import datetime
def iterate_folders(input_folder, second_script_path):
# Create a log file
log_file = os.path.join(r"C:\temp", "MDB_to_GDB_log_{}.txt".format(datetime.now().strftime('%Y%m%d_%H%M%S')))
def log_message(message):
print(message)
with open(log_file, 'a') as log:
log.write(message + '\n')
# Iterate through all subfolders and files
for root, dirs, files in os.walk(input_folder):
for file in files:
# Check if the file is a .mdb file
if file.endswith(".mdb"):
mdb_path = os.path.join(root, file)
gdb_name = os.path.splitext(file)[0] + ".gdb"
gdb_path = os.path.join(root, gdb_name)
log_message("Processing MDB: " + mdb_path)
# Call the second script to compare and copy items
subprocess.call(["python", second_script_path, mdb_path, gdb_path, log_file])
# Example usage
input_folder = r"C:\temp\FolderWithMDBs"
second_script_path = r"C:\Temp\Script 4.py" # Specify the full path to the second script
if not os.path.exists(input_folder):
def log_message(message):
print(message)
with open(log_file, 'a') as log:
log.write(message + '\n')
log_message("The path " + input_folder + " does not exist.")
else:
iterate_folders(input_folder, second_script_path) Script 4 import os
import sys
import arcpy
def compare_and_copy_items(mdb_path, gdb_path, log_file):
def log_message(message):
print(message)
with open(log_file, 'a') as log:
log.write(message + '\n')
try:
# Ensure the GDB exists
if not arcpy.Exists(gdb_path):
log_message("GDB does not exist: " + gdb_path)
return
# List all datasets in the .mdb
arcpy.env.workspace = mdb_path
mdb_datasets = arcpy.ListDatasets("*", "All") or []
log_message("MDB Datasets: " + str(mdb_datasets))
mdb_fcs = arcpy.ListFeatureClasses("*", "All") or []
log_message("MDB Feature Classes: " + str(mdb_fcs))
mdb_tables = arcpy.ListTables("*", "All") or []
log_message("MDB Tables: " + str(mdb_tables))
# List all datasets in the .gdb
arcpy.env.workspace = gdb_path
gdb_datasets = arcpy.ListDatasets("*", "All") or []
log_message("GDB Datasets: " + str(gdb_datasets))
gdb_fcs = arcpy.ListFeatureClasses("*", "All") or []
log_message("GDB Feature Classes: " + str(gdb_fcs))
gdb_tables = arcpy.ListTables("*", "All") or []
log_message("GDB Tables: " + str(gdb_tables))
# Compare and copy missing datasets
for dataset in mdb_datasets:
if dataset not in gdb_datasets:
try:
arcpy.management.Copy(os.path.join(mdb_path, dataset), os.path.join(gdb_path, dataset))
log_message("Copied dataset: " + dataset)
except Exception as e:
log_message("Error copying dataset {}: {}".format(dataset, str(e)))
# Compare and copy missing feature classes
for fc in mdb_fcs:
if fc not in gdb_fcs:
try:
arcpy.management.Copy(os.path.join(mdb_path, fc), os.path.join(gdb_path, fc))
log_message("Copied feature class: " + fc)
except Exception as e:
log_message("Error copying feature class {}: {}".format(fc, str(e)))
# Compare and copy missing tables, excluding those starting with 'dbo_'
for table in mdb_tables:
if not table.startswith("dbo_") and table not in gdb_tables:
try:
arcpy.management.Copy(os.path.join(mdb_path, table), os.path.join(gdb_path, table))
log_message("Copied table: " + table)
except Exception as e:
log_message("Error copying table {}: {}".format(table, str(e)))
else:
log_message("Skipping table: " + table)
except Exception as e:
log_message("Error processing {}: {}".format(mdb_path, str(e)))
if __name__ == "__main__":
if len(sys.argv) != 4:
print("Usage: python compare_and_copy_items.py <mdb_path> <gdb_path> <log_file>")
else:
mdb_path = sys.argv[1]
gdb_path = sys.argv[2]
log_file = sys.argv[3]
compare_and_copy_items(mdb_path, gdb_path, log_file) Conclusion I hope that this may be use to someone in their migration efforts. It took a fair bit of trial and error to make it work and consider all the unique situations that occur with differing data management strategies over time, so will likely need to be customised for your particular need. A solid starting point and methodology though will hopefully make your efforts a lot simpler!
... View more
02-12-2025
06:59 AM
|
3
|
4
|
1381
|
|
POST
|
Well, so far it all seems to be happy enough with the updated coordinate systems. Views still showing the original in ArcGIS Online's metadata, but they display correctly when loaded into Pro so thats what matters.
... View more
02-11-2025
04:51 PM
|
0
|
0
|
2102
|
|
POST
|
I too have found Pro to be a bit stubborn with the caching of the Catalog pane. Usually though, right clicking on the GDB and selecting refresh is enough to make it show any new datasets. Maybe something else to consider - at the end of your models, include the "Collect Values" tool. Set your output dataset as the input to "Collect Values" and set the output from "Collect Values" as a parameter. When you run your model, it should then add the output dataset/s to your map display automatically. Doing this may even force the GDB to auto refresh, but I'm not sure.
... View more
02-11-2025
04:47 PM
|
0
|
1
|
2161
|
|
POST
|
Have you done any software updates in the intervening period? We've found models don't behave the same after updates (sometimes) due to tweaks in the architecture. One thing to check would be to see if any of the outputs have somehow changes to "Intermediate Data" and are getting wiped after the tool is finished. (Tip - run in Edit mode to diagnose this and it won't wipe the intermediate data).
... View more
02-10-2025
11:26 PM
|
0
|
4
|
2205
|
|
IDEA
|
Read here for more info: Inline variable substitution—ArcGIS Pro | Documentation
... View more
02-10-2025
08:07 PM
|
0
|
0
|
1258
|
|
IDEA
|
You can still use generated variables in the output name, it's just not a hardwired part of the tool because there's no separate feature class 'path' and 'name' fields. To do this, if your Calculate Value step output is called "Value", then in the output pathway of Export Features, you could enter C:\my\folder\path\my.gdb\%Value% and that will use the calculated value from that step (probably wise to set that as a precondition to the Export Features tool to ensure it has run first).
... View more
02-10-2025
06:59 PM
|
0
|
0
|
1265
|
|
POST
|
I think it was in the Python space - can't quite remember to be honest (part of why I went looking!)
... View more
02-10-2025
06:51 PM
|
0
|
1
|
593
|
|
IDEA
|
I've just done a bit of testing myself on this relative vs absolute pathways thing and I'm even more confused now than I was before! It seems that data stored to a local drive (e.g. C) is updated when a project is Copy/Pasted but data on a Network Drive (e.g. G) is not. This isn't what I read above. UNC pathways also remained the same (as expected). I fully expected my Network data to be out of whack as well after moving the project. Really struggling to find the consistency!
... View more
02-06-2025
11:41 PM
|
0
|
0
|
3688
|
|
IDEA
|
@agjackson1 ad @JoyDRoberts It sounds like your folders that say one thing but actually mean another are showing an inaccurate (potentially the original) alias instead of the current folder name after having moved an aprx? Maybe right-click and see if you can remove the Alias. Also, under Project > Options > Catalog Browsing you may want to change this setting to "Complete path" instead of "Folder name only". Before: After: (note the missing drive letter names for network drives but not local drives - no idea whats going on there)
... View more
02-06-2025
09:49 PM
|
0
|
0
|
3697
|
|
POST
|
I've just added an extra "Solution" to the post which simplifies the findings down to a couple of clear 1 liners. It isn't a solution to how to use Absolute paths all the time, but it does clearly explain the behaviour around 'moving' projects and what happens to the relative pathways. For what it's worth, we manage a lot of ArcGIS Pro project templates, stored on a G drive. Our users have been instructed to Open and Save As these to their desired location so that all pathways are maintained. If they were to Copy/Paste the project instead, all the pathways mapped to a Drive letter would be updated (relative pathways). I'm about to do some more testing on UNC pathways - I've seen text stating that these don't change, which may be an (annoying) solution for us. All our GIS data is stored on network Drives (G & V) or in ArcGIS Online as a feature service (no issues there at least).
... View more
02-06-2025
08:43 PM
|
0
|
0
|
1162
|
|
IDEA
|
Hi @JesseWickizer. Yesterday I ran into a small "bug" with the new scale bar visibility features. I'd spent some time creating a set of 3 scale bars with different Visibility ranges (<1:10000 / 1:10k - 1:30k / >1:30000). For the middle interval, I had the scales set to 1:10001 - 1:29999 with the upper and lower bars set to the round 1:10000 (Min) & 1:30000 (Max) values. However, the problem was that the scale bars weren't showing up when I zoomed to a "Max" scale value. TODAY however, I went back in to get more screenshots for this post, and now they work as expected. So, instead of asking for guidance around whether this should be logged as a bug or is expected behaviour, I'm instead going to suggest that you use thresholds that are unlikely to never be used, instead of clean breaks like I have, JUST IN CASE!
... View more
02-06-2025
06:52 PM
|
0
|
0
|
3304
|
|
IDEA
|
Great idea and a cool setting I've learnt about too. May need to implement this in a few map templates.
... View more
02-06-2025
06:31 PM
|
0
|
0
|
1391
|
|
POST
|
Hi team. I submitted a blog post for review a week or 2 ago but can't seem to find where it is or if it's been published, reviewed, rejected, etc. Is anyone able to point out how I can find where it's at? I definitely wanted it reviewed as it's my first time writing a blog and wasn't sure if I was on the right track or not regarding content and quality.
... View more
02-06-2025
04:26 PM
|
1
|
3
|
2665
|
| Title | Kudos | Posted |
|---|---|---|
| 1 | a week ago | |
| 3 | 2 weeks ago | |
| 6 | a week ago | |
| 2 | a week ago | |
| 2 | a week ago |
| Online Status |
Offline
|
| Date Last Visited |
Tuesday
|