4

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
        continue
    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]
    else:
        pprwidth = pixwidth / 254.0
        pprheight = pixheight / 254.0
        if pprwidth > pprheight:
            pprorienttxt = 'LANDSCAPE'
        else:
            pprorienttxt = 'PORTRAIT'
    if pprorienttxt == 'PORTRAIT':
        pprorient = 1
    else:
        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)
    pMapDoc.Open(destmxd)
    pPgLayout = pMapDoc.PageLayout
    pPgLayout3 = CType(pPgLayout, esriCarto.IPageLayout3)
    pPage = pPageLayout3.Page
    pPage.Units(esriInches)
    pPage.PutCustomSize(pprwidth, pprheight)
    pPage.Orientation(pprorient)
    pMap = pMapDoc.Map
    pMap.MapUnits(esriInches)
    pMap.SetPageSize(pprwidth, pprheight)
    pMap.ReferenceScale(1.0/scale)
    pMap.MapScale(1.0/scale)

    pMapDoc.Close()
    i = i + 1
f.close()
  • Can you upload your code? – Hornbydd Nov 12 '13 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.) – celticflute Nov 12 '13 at 18:13
  • No sorry, what I wrote above is not correct. For MapDocument, the properties can be accessed directly. – user30624 May 26 '14 at 17:08
2

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():
    try:
        # 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
            mxd.save()
    except:
        print "A critical error occurred during the execution of this Script, cannot continue!"

if __name__ == '__main__':
    arcpy.env.overwriteOutput=True
    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. – celticflute Nov 13 '13 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. – celticflute Nov 13 '13 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. – celticflute Nov 13 '13 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 Nov 13 '13 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. – celticflute Nov 18 '13 at 1:59

protected by Community Oct 26 '14 at 6:07

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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