import os
import tempfile
import warnings
import arcpy
from amaptor.version_check import log, mp, PRO, mapping
from amaptor.errors import LayerNotFoundError, NotSupportedError
from amaptor.constants import _PRO_BLANK_TEMPLATE
def _import_mxd_to_new_pro_project(mxd, blank_pro_template=_PRO_BLANK_TEMPLATE, default_gdb="TEMP"):
"""
Handles importing an ArcMap Document into an ArcGIS Pro Project. Default Geodatabase is "TEMP" by default, indicating
a temporary gdb should be created. It can also be "KEEP" to leave it alone, or it can be a path
:param mxd:
:param blank_pro_template:
:param default_gdb:
:return:
"""
log.warning("WARNING: Importing MXD to new Pro Project - if you call .save() it will not save back to original MXD. Use .save_a_copy('new_path') instead.")
# can safely assume that if this is called, we're running on Pro and that's already been checked
# copy blank project to new location - template is 1.3+
blank_project = mp.ArcGISProject(blank_pro_template)
new_temp_project = tempfile.mktemp(".aprx", "pro_project_import")
blank_project.saveACopy(new_temp_project)
del(blank_project)
# strictly speaking, we don't need to destroy and recreate - should be able to edit original without saving - doing this just to keep things clear
project = mp.ArcGISProject(new_temp_project)
project.importDocument(mxd, include_layout=True)
if default_gdb != "KEEP": # if we're supposed to modify it
if default_gdb == "TEMP":
new_default_gdb = tempfile.mktemp(prefix="amaptor_default_geodatabase", suffix=".gdb")
arcpy.CreateFileGDB_management(os.path.split(new_default_gdb)[0], os.path.split(new_default_gdb)[1])
project.defaultGeodatabase = new_default_gdb
else: # if it's not KEEP or TEMP it must be a path
project.defaultGeodatabase = default_gdb
project.save()
# return the project path, setup will continue from here
return new_temp_project
[docs]def make_layer_with_file_symbology(feature_class, layer_file, layer_name=None):
"""
Given a feature class or raster and a template layer file with symbology, returns a new Layer object that has the layer
from the layer file with the feature class as its data source. Optionally, layer can be renamed with layer_name
This function will be scheduled for deprecation as it's superceded by using the Layer object with a template layer.
More testing needs to be done and maybe more code written in ArcMap to make the alternative apply to both cases. This will
be deprecated when that's complete
:param feature_class:
:param layer_file:
:param layer_name:
:return:
"""
#if PRO: # we'll actually issue the warning once there's a better cross-platform solution. For now, comment it out
# warnings.warn("make_layer_with_file_symbology is deprecated in ArcGIS - use Layer objects with a template layer instead", DeprecationWarning)
layer = None
if PRO:
layer_file = mp.LayerFile(layer_file)
for layer in layer_file.listLayers(): # gets the first layer in the layer file
break
else:
layer = mapping.Layer(layer_file)
if layer is None:
raise LayerNotFoundError("No layer available for copying from layer file")
elif not layer.supports("DATASOURCE"):
raise NotSupportedError("Provided layer file doesn't support accessing or setting the data source")
if PRO:
layer.dataSource = feature_class
else:
desc = arcpy.Describe(feature_class)
if desc.extension and desc.extension != "": # get the name with extension for replacing the data source
name = "{}.{}".format(desc.baseName, desc.extension)
else:
name = desc.baseName
layer.replaceDataSource(desc.path, get_workspace_type(feature_class), name)
if layer_name:
layer.name = layer_name
return layer
[docs]def reproject_extent(extent, current_extent):
"""
Changes an extent from its current spatial reference to the spatial reference on another extent object
:param extent:
:param current_extent:
:return:
"""
return extent.projectAs(current_extent.spatialReference)
[docs]def get_workspace_type(dataset_path, factory_type=False):
"""
Gives us a workspace type that's usable for layer.replaceDataSource in ArcMap based on a dataset path
:param dataset_path: path to dataset to return workspace type from
:param factory_type: boolean flag indicating whether to return the workspace_factory type or the standard dataset type - workspace factory values are not yet fully implemented
:return:
"""
"""
Full list of possible factory values is the following in Pro, per Craig Williams:
Access, De-limited Text File, File Geodatabase, OLE Database, Raster, ArcInfo, SDE, Shape File, LASDataset, Sql, TrackingServer, NetCDF, SqlLite, FeatureService, Cad, Excel, Street Map, SDC, Custom, WFS
"""
if factory_type:
attr = "factory"
else:
attr = "workspace_type"
prog_id_mapping = {
"esriDataSourcesGDB.AccessWorkspaceFactory": {
"workspace_type": "ACCESS_WORKSPACE",
"factory": "Access" # not positive this is what this value is - can confirm by loading a MDB layer and testing layer.connectionProperties
},
"esriDataSourcesGDB.FileGDBWorkspaceFactory": {
"workspace_type": "FILEGDB_WORKSPACE",
"factory": "File Geodatabase"
},
"esriDataSourcesGDB.InMemoryWorkspaceFactory": {
"workspace_type": "NONE",
"factory": "",
},
"esriDataSourcesGDB.SdeWorkspaceFactory": {
"workspace_type": "SDE_WORKSPACE",
"factory": "SDE"
}
}
type_mapping = {
"SHAPEFILE": {
"workspace_type": "SHAPEFILE_WORKSPACE",
"factory": "Shape File" # found by loading a shapefile layer and testing layer.connectionProperties
},
"EXCEL": {
"workspace_type": "EXCEL_WORKSPACE",
"factory": "Excel"
},
"TEXT": {
"workspace_type": "TEXT_WORKSPACE",
"factory": "De-limited Text File"
},
"RASTER": {
"workspace_type": "RASTER_WORKSPACE",
"factory": "Raster"
},
"TIN": {
"workspace_type": "TIN_WORKSPACE",
"factory": ""
}
}
dataset_desc = arcpy.Describe(dataset_path)
workspace = dataset_desc.path
workspace_desc = arcpy.Describe(workspace)
if workspace_desc.workspaceFactoryProgID.replace(".1", "") in prog_id_mapping: # if we have the specific name for it here, return that first
return prog_id_mapping[workspace_desc.workspaceFactoryProgID][attr]
elif workspace_desc.workspaceType == "FileSystem":
if dataset_desc.extension == "shp":
return type_mapping["SHAPEFILE"][attr]
elif dataset_desc.extension in ("xls", "xlsx"):
return type_mapping["EXCEL"][attr]
elif dataset_desc.extension in ("tab", "csv", "txt"): # probably not the best way to handle this
return type_mapping["TEXT"][attr]
elif dataset_desc.dataType == "Raster":
return type_mapping["RASTER"][attr]
elif dataset_desc.dataType == "Tin":
return type_mapping["TIN"][attr]
elif workspace_desc.workspaceFactoryProgID == "":
return type_mapping["SHAPEFILE"][attr] # if we get to here without returning, it's likely a shapefile - there are a few items missing from this conditional - CAD, VPF, etc
[docs]def get_workspace_factory_of_dataset(dataset_path):
"""
Provides the workspace factory type of a provided dataset - a convenience function that calls get_workspace type with the appropriate flag in the backgroun
:param dataset_path: path to dataset to return workspace_factory value of
:return:
"""
return get_workspace_type(dataset_path=dataset_path, factory_type=True)