Manipulating Layers within a Map Package using Arcpy
The other day I was given a map package file (that included data) and told that the dataset was too big and needed to be clipped before it was released. The data to be distributed only supposed to cover a few square miles in a square extent frame, yet the datasets originally extend far beyond those limits. Although some masking parameters went into the input of the Data Management âPackage Mapâ tool, it doesnât seem to clip the data as expected. What appears to happen is that ArcGIS performs a selection on anything inside the clipping parameters and extracts that selection for the package. This makes perfect sense for some political information (municipality boundaries) where I donât want to force data to be the extent of my project. However, this leaves me with dangling linear features! The ArcMap 10.0 project consisted of about 35 layers â no way that I would want to process this data myself, especially if there is more than one map package file. So, I started to dig into ArcPyâs Mapping module to process each layer in the document. However, in the [too many] hours I spent trying to figure strange problems out, I thought it be worth the while to pass on what Iâve learned. Lesson 1: Scheme Locks It is possible to unpack the map package in ArcPy, but doing so may cause some schema locks on the data. Later, I realized that there is an âExtract Packageâ tool. Iâm not sure how different this is to my method or how many similar problems might be experienced with using it. For all I know, it would have likely removed all of my headaches! The way in which I unpacked the map was as follows:
import arcpy, mapping inFile = âC:\\ ⌠myLocalSaveSpot⌠.mpkâ MPK_file = inFile MPK = arcpy.mapping.MapDocument(MPK_file) MXD = MPK.filePath del MPK myMXD = arcpy.mapping.MapDocument(MXD)
It is crucial that you delete the MPK variable. While this is still in memory, it puts a scheme lock over the data that it extracts. Thus, if you try to process any of the data, they will error out. Lesson 2: Processing and updating data sources The next step I needed to do (the important one) was to update the data by clipping it. It is again important to note my purpose here â I wanted to replace the data that existed in my map package. This is a bit âoffâ because the purpose of the map package is to take a snapshot of your data and either archive or share it. Indeed, I need to share a snapshot of my data, but it needs to be clean! The second step here is to update the data. I donât want to remove the layer from my ArcMap documents and re-add the new one. This would destroy the symbology that is set. So, what needs to happen is that the original data must be deleted and the ArcMap layer must be resourced. I had a heck of a time using the arcpy.mapping.replaceDataSource to work (I think my problem was trying to set the incorrect workspace typeâŚ), so I came up with another method. I clipped the data and saved it into the delightful memory workspace. I then deleted the original clip layer and replaced it with the clipped layer that was stored in memory. I found that this strategy was efficient because I wasnât having to delete, save, rename, delete, etc. many times. The strategy I used was below :
df = arcpy.mapping.ListDataFrames(doc) lyrs = arcpy.mapping.ListLayers(df[1]) #I have several data frames in the document featLyrs = []#Create an empty list to store all feature class layers for lyr in lyrs: if lyr.isFeatureLayer(): featLyrs.append(lyr) #Need to extract only feature classes. Raster datasets will use a different clipping tool clpLyr = arcpy.mapping.ListLayers(df[1])[-1]#my clip extent is the last layer in the data frame for clp in featLyrs: if (int(str(arcpy.GetCount_management(clp)))>0: #Donât want to clip an empty feature class savePath = clp.dataSource arcpy.Clip_analysis(savePath, clpLyr, âin_memory\\tempâ) arcpy.Delete_management(clp.dataSource) arcpy.CopyFeatures_management(âin_memory\\tempâ, savePath)#saves the in_memory fc to the exact location and name as the original layer arcpy.Delete_management(âin_memory\\tempâ)
It is also possible to parse out raster or group layers if you need to. The call lyr.isRasterLayer() will return a true if, well, the dataset is a raster. Likewise, you can omit examining other things like group layers by the call lyr.isGroupLayer(). This fits well into the code while looping through data frames and the layers within. Lesson 3: Repackaging the Map Package Of course, I cannot change the fact that Map Packages are intended to be a snapshot of my data. This means that the data that I process will only be temporary and not be included in the original map package file if I share it. Unfortunately, the map needs to be repackaged. But what to do about that original, messy one? The last part of this code shows how to package the MXD into the original MPK file (kinda). Beware that if your dataset is large, this will still take some time (but if you had already created an MPK file, Iâm sure you are well aware!).
MPK_revise = MPK_file[:-4]+"_revise.mpk" arcpy.PackageMap_management(doc.filePath, MPK_revise) os.remove(MPK_file) os.rename(MPK_revise, MPK_file)
Thatâs all I have for now. I wish I could come up with some way to move layers from one data frame to another and add/remove data frames!!








