6

I have a mass of KML files, approx 350+ that I need in a single fGDB. I lifted a string of code from ESRI help to attempt to do this - I got it working to the point of creating all the individual gGDBs (one per KML) but its failing with an invalid character in a variable somewhere. Below is the code, and then the error that it throws out. I've added some print commands to show whats in the variables right before it fails. Help? Also, this is my first time trying to use python for actual work, so I apologize in advance for n00bish mistakes.

# Name: BatchKML_to_GDB.py
# Description: Converts a directory of KMLs and copies the output into a single fGDB.
# A 2 step process: first convert the KML files, and then copy the featureclases
# Import system models
import arcpy, os
# Set workspace (where all the KMLs are)
 arcpy.env.workspace="C:\\Users\\mperez\\Desktop\\IT-Paths"
# Set local variables and location for the consolidated file geodatabase
outLocation = "V:\2155円\\active\215510456円\\GDB"
MasterGDB = r'AllKMLLayers.gdb'
MasterGDBLocation = os.path.join(outLocation, MasterGDB)
# Create the master FileGeodatabase
 arcpy.CreateFileGDB_management(outLocation, MasterGDB)
# Convert all KMZ and KML files found in the current workspace
 for kmz in arcpy.ListFiles('*.KM*'):
 print "CONVERTING: " + os.path.join(arcpy.env.workspace,kmz)
 arcpy.KMLToLayer_conversion(kmz, outLocation)
# Change the workspace to fGDB location
arcpy.env.workspace = outLocation
# Loop through all the FileGeodatabases within the workspace
wks = arcpy.ListWorkspaces('*', 'FileGDB')
# Skip the Master GDB
wks.remove(MasterGDBLocation)
for fgdb in wks: 
 # Change the workspace to the current FileGeodatabase
 arcpy.env.workspace = fgdb 
 # For every Featureclass inside, copy it to the Master and use the name from the original fGDB
 featureClasses = arcpy.ListFeatureClasses('*', '', 'Placemarks')
 for fc in featureClasses:
 print r"COPYING: " + fc + r" FROM: " + fgdb 
 fcCopy = fgdb + os.sep + 'Placemarks' + os.sep + fc
 print r"fcCopy = " + fcCopy
 print r"MasterGDBLocation = " + MasterGDBLocation
 print r"fgdb = " + fgdb
 arcpy.FeatureClassToFeatureClass_conversion(fcCopy, MasterGDBLocation, fgdb[fgdb.rfind(os.sep)+1:-4])
# Clean up
del kmz, wks, fc, featureClasses, fgdb

Output from shell showing error:

COPYING: Points FROM: V:2155円\active215510456円\GDB\canal-move-2-golden-gate-blvd.gdb
fcCopy = V:2155円\active215510456円\GDB\canal-move-2-golden-gate-blvd.gdb\Placemarks\Points
MasterGDBLocation = V:2155円\active215510456円\GDB\AllKMLLayers.gdb
fgdb = V:2155円\active215510456円\GDB\canal-move-2-golden-gate-blvd.gdb
Traceback (most recent call last):
 File "C:\Users\mperez\Desktop\batch_kml_to_gdb.py", line 46, in <module>
 arcpy.FeatureClassToFeatureClass_conversion(fcCopy, MasterGDBLocation, fgdb[fgdb.rfind(os.sep)+1:-4])
 File "C:\Program Files (x86)\ArcGIS\Desktop10.1\arcpy\arcpy\conversion.py", line 1547, in FeatureClassToFeatureClass
 raise e
ExecuteError: ERROR 000354: The name contains invalid characters
Failed to execute (FeatureClassToFeatureClass).
Felipe D.
2,8494 gold badges20 silver badges39 bronze badges
asked Mar 22, 2013 at 21:57
1
  • One other this, could you add which version of ArcGIS you're using to the tags as well? (and +1 for actually posting the stacktrace - you'd be amazed at how often this doesn't happen) Commented Mar 22, 2013 at 22:47

3 Answers 3

5

In the traceback you can see where the error in the code was raised (the last line), and every function in the call leading to that error - just go backwards up the list.

From this you can see that the error was in line 46 of your code;

arcpy.FeatureClassToFeatureClass_conversion(fcCopy, MasterGDBLocation, fgdb[fgdb.rfind(os.sep)+1:-4])

You know the third argument in Feature Class to Feature Class is the feature class name. Your first step should be to take a look at os.path.basename and os.path.splitext for cleaner ways to get the name.

Your second step should be to look at ValidateTableName in arcpy, which when you pass it the proposed name and workspace, will return a valid name.

PolyGeo
65.5k29 gold badges115 silver badges349 bronze badges
answered Mar 22, 2013 at 22:46
1
  • 2
    What om_henners gave you is the solution, but just to understand why this particular name, canal-move-2-golden-gate-blvd failed, - is an invalid character. The list is `~@#$%^&()-+=,{}.![]; /\:*?'<>| Commented Mar 25, 2013 at 13:23
4

This is not meant to be an answer, just a contribution (taken from my comment on om_henners answers)

import arcpy
invalidcharacters = """`~@#$%^&()-+=,{}.![]; /\:*?'<>|"""
valid = arcpy.ValidateTableName(invalidcharacters)
print valid

Output:

______________________________

(that means every character in `~@#$%^&()-+=,{}.![]; /\:*?'<>| is an invalid character)

answered Mar 25, 2013 at 13:26
2
  • Thanks - I wasn't sure of the complete list of invalid charactes, so I didn't want to mislead anyone! Commented Mar 26, 2013 at 3:48
  • I'm not sure that is complete either, but that's all I have been able to find :) I thought " was invalid too, but it was not for fgdb when I tested it. Commented Mar 26, 2013 at 13:11
1

I know this is a very old question by now, but here is a script I've put together that can deal with a bunch of corner cases of the KML/KMZ to GDB conversion process.

I hope someone else finds this useful!

Btw, not sure if this is obvious, but it might be worth mentioning that the code below was written for Python 3.

# This script was heavily modified from the script available in the ArcGIS Pro documentation:
# https://pro.arcgis.com/en/pro-app/latest/tool-reference/conversion/kml-to-layer.htm
# Also includes information from the following GIS Stack Overflow question: 
# https://gis.stackexchange.com/questions/55259/python-script-to-convert-a-mass-of-kmls-to-a-single-fgdb?noredirect=1&lq=1
# Converts a directory of KMZs/KMLs and copies the output into a single GDB
# Import system modules
import arcpy
import os
import pathlib
import string
def export_multiple_kmzs_to_single_gdb(kmz_folder, output_gdb, temp_folder):
 '''
 Function that reads an input folder with multiple KMZ/KML files and exports
 all the geometric data contained in those files into a single GDB file.
 
 Parameters
 ----------
 kmz_folder : str
 The input folder that contains all the KMZs/KMLs 
 that will be converted
 output_gdb : str
 Path for the consolidated GeoDataBase (.gdb) file
 temp_folder : str
 Temporary folder where multiple GDB and LRYX files will be generated
 and later deleted.
 
 Returns
 -------
 None.
 
 Outputs
 -------
 This function creates a ".gdb" file that contains all the geometric 
 features from the KMZ/KML files in the input directory. 
 The data from the KMZs/KMLs are all blended into one layer per geometry 
 type. For example, suppose the following file structure:
 -"A.kmz" has points, 
 -"B.kmz" has points and linestrings,
 -"C.kmz" has points and polygons,
 -"D.kmz" has points. 
 In that case, the output GDB file will have 3 layes:
 -One layer containing all the points (called "All_Points"), another 
 containing all the polylines (called "All_Polylines"), and yet another 
 containing all the polygons (called "All_Polygons").
 
 '''
 # Fishing out the output folder and the GDB filename
 output_gdb_path = pathlib.Path(output_gdb)
 output_folder = str(output_gdb_path.parent)
 master_gdb = output_gdb_path.stem + output_gdb_path.suffix
 
 # Creating the temp folder in case it doesn't exist
 temp_newly_created = False
 if not os.path.exists(temp_folder):
 # Flag to indicate that this folder should be deleted at the end of
 # the process
 temp_newly_created = True
 os.mkdir(temp_folder)
 
 # Creating master GDB. This will store many temporary layers and the 
 # finalized combined layers.
 master_gdb_fullpath = os.path.join(output_folder, master_gdb)
 if os.path.exists(master_gdb_fullpath):
 arcpy.Delete_management(master_gdb_fullpath)
 arcpy.CreateFileGDB_management(output_folder, master_gdb)
 
 # Get list of all KMZs/KMLs in the input folder
 list_of_kmzs = [f for f in os.listdir(kmz_folder) 
 if ((f[-4:].lower()=='.kmz') or (f[-4:].lower()=='.kml'))]
 
 # Set that contains the names of the consolidated layers created thus far
 # (i.e., "All_Lines", "All_Polygons", ...). This set starts empty and 
 # gets populated as the execution progresses. 
 consolidated_layers = set()
 
 # Looping over every KMZ/KML file
 for i,kmz in enumerate(list_of_kmzs):
 # Convert KMZ/KML file into a separate temporary GDB
 kmz_fullpath = os.path.join(kmz_folder, kmz)
 kmz_gdb_name = os.path.join(temp_folder, kmz)[:-3] + 'gdb'
 if os.path.exists(kmz_gdb_name):
 arcpy.Delete_management(kmz_gdb_name)
 print(f"CONVERTING: {kmz_fullpath}")
 arcpy.env.workspace = kmz_folder
 arcpy.KMLToLayer_conversion(kmz_fullpath, temp_folder)
 
 feature_classes = arcpy.ListFeatureClasses('*', '', 'Placemarks')
 
 arcpy.env.workspace = kmz_gdb_name
 
 # Looping over every Featureclass from the newly-created GDB
 feature_classes = arcpy.ListFeatureClasses('*', '', 'Placemarks')
 
 # If the input KMZ/KML is empty, the `feature_class` object from the 
 # function above will contain None. This needs to be fixed for the 
 # script to work.
 if feature_classes is None:
 feature_classes = []
 
 for this_fc in feature_classes:
 
 arcpy.env.workspace = kmz_gdb_name
 
 # Adding the "kmz_filename" column
 arcpy.management.AddField(in_table=this_fc,
 field_name='kmz_filename', 
 field_type='TEXT', 
 field_length=1024)
 
 # Updating the values of the "kmz_filename" column
 with arcpy.da.UpdateCursor(this_fc,'kmz_filename') as rows:
 for row in rows:
 row[0] = kmz
 rows.updateRow(row)
 del(row)
 del(rows)
 
 # Creating the name for the new layer
 new_layer_name = kmz_gdb_name[kmz_gdb_name.rfind(os.sep) + 1:-4]
 
 # Fixing some invalid characters in the layer name
 valid_characters = set(string.ascii_letters + string.digits + '_')
 invalid_characters = set([x for x in new_layer_name if x not in valid_characters])
 for this_char in invalid_characters:
 new_layer_name = new_layer_name.replace(this_char,'_')
 new_layer_name = arcpy.ValidateTableName(new_layer_name)
 
 # Ensuring that the first character isn't a number
 if new_layer_name[0] in string.digits:
 new_layer_name = '_' + new_layer_name
 
 # Copying the Featureclass from the newly-created GDB to the 
 # Master GDB
 print(f"COPYING: {this_fc} FROM: {kmz_gdb_name}")
 fcCopy = os.path.join(kmz_gdb_name, 'Placemarks', this_fc)
 
 arcpy.FeatureClassToFeatureClass_conversion(
 fcCopy, master_gdb_fullpath, new_layer_name)
 
 # Copying the features from each individual Featureclass into the 
 # main "All_Points" or "All_Polylines" Featureclasses (i.e., the
 # consolidated layers).
 master_layer_name = f'All_{this_fc}'
 arcpy.env.workspace = master_gdb_fullpath
 print(f"COPYING: {this_fc} TO: {master_layer_name}")
 
 # If the consolidated layer has already been created, append 
 # the KMZ/KML's data to it. If not, the layer needs to be created
 # and the set of consolidated layers needs to be updated.
 if master_layer_name in consolidated_layers:
 arcpy.management.Append(new_layer_name, master_layer_name)
 else:
 arcpy.CopyFeatures_management(new_layer_name, master_layer_name)
 consolidated_layers.add(master_layer_name)
 
 # Delete the temporary Featureclass layer inside the master GDB
 arcpy.Delete_management(new_layer_name)
 
 # Deleting the newly-generated GDB and lyrx files
 arcpy.env.workspace = master_gdb_fullpath
 if os.path.isdir(kmz_gdb_name):
 arcpy.Delete_management(kmz_gdb_name)
 lyrx_filename = kmz_gdb_name[:-3] + 'lyrx'
 if os.path.isfile(lyrx_filename):
 os.remove(lyrx_filename)
 
 # If the temp folder didn't exist at the beginning of this process, it
 # should be deleted at the end.
 if temp_newly_created:
 os.rmdir(temp_folder)
 
answered Aug 3, 2021 at 21:54

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.