I need to change the spatial reference of an existing MXD programmatically. In ArcMap UI, I would take the following steps: 1) Open the data frame properties and go to the Coordinate System tab; 2) click on little globe button (Add Coordinate System) and choose New Projected Coordinate System; 3) choose Mercator; 4) enter custom name for the coordinate system; 5) change the Standard_Parallel_1 parameter to the appropriate value; and 6) Save/PressOK. Ultimately, I need to automate this process over hundreds of MXDs.

I am really very confused about where even to start. My research has turned up a lot of references to IProjectedCoordinateSystemEdit and related classes and interfaces and to reading/writing parameters to arrays with IParamter and related, but I am finding a lot of the information confusing. So, which Interfaces/Classes should I start with, and what is the most concise way of accomplishing automation of the process I describe above?

My code currently opens the MXD to be processed in an IMapDocument and changes a suite of PageLayout and Map settings. What is left is the changing of the spatial reference. Thanks for any guidance you can offer.

UPDATE (@Hornbydd) Here is the Python main body code. (BTW, when accessing ArcObjects from Python, one thing that Python can't do is implement interfaces, so responding to events and extending ArcGIS isn't possible. I have yet to find any com objects that it can't access, though.)

f = open(chartprops, 'r')

i = 0
for chrt in f:
    if i == 0:  #skip the header
        i = i + 1
    props = chrt.split(',')
    chartnm = props[0]
    scale = float(props[1])
    latorig = props[2]
    pixwidth = float(props[3])
    pixheight = float(props[4])
    if len(props) > 5:
        pprwidth = float(props[5])
        pprheight = float(props[6])
        pprorienttxt = props[7]
        pprwidth = pixwidth / 254.0
        pprheight = pixheight / 254.0
        if pprwidth > pprheight:
            pprorienttxt = 'LANDSCAPE'
            pprorienttxt = 'PORTRAIT'
    if pprorienttxt == 'PORTRAIT':
        pprorient = 1
        pprorient = 2

    #copy template chart MXD to chart output location
    #==== WARNING! ====
    # If the mxd already exists in the detination folder,
    # it will be overwritten
    destmxd = outloc + os.path.sep + chartnm + '.mxd'
    shutil.copy(templt, destmxd)

    pMapDoc = NewObj(esriCarto.MapDocument, esriCarto.IMapDocument)
    pPgLayout = pMapDoc.PageLayout
    pPgLayout3 = CType(pPgLayout, esriCarto.IPageLayout3)
    pPage = pPageLayout3.Page
    pPage.PutCustomSize(pprwidth, pprheight)
    pMap = pMapDoc.Map
    pMap.SetPageSize(pprwidth, pprheight)

    i = i + 1
  • Can you upload your code?
    – Hornbydd
    Commented Nov 12, 2013 at 8:52
  • @Hornbydd - I have now posted the code that you requested. Pretty simple stuff. I just don't know where is the best place to start in the object model. (Sorry. I was on a different computer and could only login with a different account. user23896 is me.) Commented Nov 12, 2013 at 18:13
  • No sorry, what I wrote above is not correct. For MapDocument, the properties can be accessed directly.
    – user30624
    Commented May 26, 2014 at 17:08

1 Answer 1


I'm not sure if you are over engineering this? Or I have simplified it in my head! I was able to go through a set of MXD's changing the spatial reference of the first dataframe with some relatively simple python code, so no ArcObjects required. Below is the code I used.

import arcpy

def main():
        # Get desired spatial reference from an existing shapefile
        fc = r"C:\temp\myData.shp"
        sr = arcpy.Describe(fc).spatialReference

        # Create a list of MXD's
        arcpy.env.workspace = r"C:\temp"
        mxdlist = arcpy.ListFiles("*.mxd")

        # Main update loop
        for m in mxdlist:
            # Create MapDocument object
            fullpath = arcpy.env.workspace + "\\" + m
            print "processing " + fullpath
            mxd = arcpy.mapping.MapDocument(fullpath)

            # Get first dataframe in map document and set spatialreference
            df = arcpy.mapping.ListDataFrames(mxd)[0]
            df.spatialReference = sr

            # Overwrite mxd
        print "A critical error occurred during the execution of this Script, cannot continue!"

if __name__ == '__main__':
  • Hmmm ... well ... Looks like I may have over-engineered it. Thnak you for the perspective check. I thought I had looked at arcpy.mapping thoroughly, and I rejected it for the ArcObjects route. Now, the question for me is "How do I create a new, custom spatial reference to assign to the DataFrame using your method above?" I will not have an existing feature class from which to copy the spatial reference. Commented Nov 13, 2013 at 2:45
  • Thanks, again. Now that I have asked the question in my comment above, I see that arcpy.mapping is probably going to be the answer to that one, too. I am embarrassed at my serious lapse in pre-question research ability. Oh well, live and learn. I shall strive to do better in the future. Best regards. Commented Nov 13, 2013 at 3:14
  • UPDATE: Creating and setting the new spatial reference through arcpy.mapping is certainly a good solution -- very fussy in ArcObjects; however, one of the other things I have to do to each new MXD is change the page settings. Unbelievably, you can't do that with arcpy/arcpy.mapping(!!!). This has been a serious oversight in arcpy for years. I knew about it before but had forgotten. Now I am painfully reminded. Oh well, back to ArcObjects after all. Commented Nov 13, 2013 at 17:28
  • 1
    Ha! Nothing is ever simple. Maybe you can use both? arcpy to set the spatial reference and a bit of ArcObjects to deal with the page setting? If it were me I would have done the ArcObjects in VB .net
    – Hornbydd
    Commented Nov 13, 2013 at 19:27
  • Yes, a combination of arcpy and ArcObjects was the final solution. I agree about doing things in .NET, but that is a whole different level of effort, and frankly I am not really up on .NET language and syntax. I think if I were to go with .NET, I would myself do it in C#. I used to do a lot of GIS automation and extension in VB 6, but the syntax has changed so much since then. For now I am pretty much just doing automation scripting, and the Python/comtypes combination is pretty quick and slick for accessing ArcObjects. Commented Nov 18, 2013 at 1:59

Not the answer you're looking for? Browse other questions tagged or ask your own question.