[GIS] ArcGIS 10 arcpy to add a WMS Service to MXD

arcobjectsarcpycomtypeslayerswms

I want to batch insert WMS layer to my arcmap MXD project with python/arcpy script.

Some issues has been unanserwed, here my "state of art":

All of these solution propose to use

  1. ArcMap to Add WMS layer manually
  2. do "Save as Layer File"
  3. reuse the .lyr to add to a new TOC by arcpy with this code

    mxd = arcpy.mapping.MapDocument("CURRENT")  
    df = arcpy.mapping.ListDataFrames(mxd)[0]   
    imagery_lyr = arcpy.mapping.Layer(r"path\to\.lyr")  
    arcpy.mapping.AddLayer(df, imagery_lyr)  
    

The lastest idea work properly with "static" WMS services.
When you have a "dynamic" changing WMS services, you need to maintain manually all the pre-generated lyr.

Does somebody know how to load WMS layer into ArcMap MXD with arcpy?

Does somebody know how to create .lyr file from WMS with arcpy?

Best Answer

This is not possible in pure arcpy.

It can be done in python, but requires the use of arcobjects. One way to access arcobjects in python is to use the comtypes package.

Firstly install comtypes and get set up with the arcobjects snippets module. See this question for links to the snippets module and for more information on accessing arcobjects from python.

Below is some code, derived from the ArcGIS documentation and the ArcMap CSW Client to generate a layer file of the WMS. You need to write the layer file to disk to be able to use ArcPy to add it to an MXD, but you can delete the file once it's been added to the map.

import sys
import arcview,arcpy
from comtypes import COMError
from snippets import GetModule, CType, NewObj

def ConnectWMS(url):
    #Luke Pinner 2014

    GetModule("esriSystem.olb")
    GetModule("esriCarto.olb")
    GetModule("esriGISClient.olb")

    import comtypes.gen.esriSystem as esriSystem
    import comtypes.gen.esriCarto as esriCarto
    import comtypes.gen.esriGISClient as esriGISClient

    ##Create an WMSMapLayer Instance - this will be added to the map later
    pWMSGroupLayer = NewObj(esriCarto.WMSMapLayer, esriCarto.IWMSGroupLayer)

    ##Create and configure wms connection name, this is used to store the connection properties
    pPropSet = NewObj(esriSystem.PropertySet, esriSystem.IPropertySet)
    pPropSet.SetProperty("URL", url)
    pConnName = NewObj(esriGISClient.WMSConnectionName, esriGISClient.IWMSConnectionName)
    pConnName.ConnectionProperties = pPropSet

    ##Use the name information to connect to the service
    pDataLayer = CType(pWMSGroupLayer, esriCarto.IDataLayer)
    pName = CType(pConnName, esriSystem.IName)
    pDataLayer.Connect(pName)

    ##Get service description, which includes the categories of wms layers
    pServiceDesc = pWMSGroupLayer.WMSServiceDescription

    return pWMSGroupLayer,pServiceDesc

def CreateWMSGroupLayer(url, outpath, visible, *layernames):
    #Luke Pinner 2014

    GetModule("esriCarto.olb")
    import comtypes.gen.esriCarto as esriCarto

    # Connect to WMS
    pWMSGroupLayer,pServiceDesc = ConnectWMS(url)

    #Find matching layers
    if layernames:
        pLayerDescs=[find_layer(pServiceDesc,l) for l in layernames]

        ##Configure the layer before adding it to the map
        pWMSGroupLayer.Clear()
        for i,pLayerDesc in enumerate(pLayerDescs):
            if pLayerDesc is None:
                # raise  Exception('Unable to find layer "%s"'%layernames[i])
                # warnings.warn('Unable to find layer "%s"'%layernames[i])
                sys.stderr.write('Warning: Unable to find layer "%s"\n'%layernames[i])
                continue
            pWMSMapLayer = pWMSGroupLayer.CreateWMSLayer(pLayerDesc)
            CType(pWMSMapLayer, esriCarto.ILayer).Visible=visible
            pWMSGroupLayer.InsertLayer(pWMSMapLayer, i)

    pLayer = CType(pWMSGroupLayer, esriCarto.ILayer)
    pLayer.Visible=visible

    ##create a new LayerFile and populate it with the layer
    pLayerFile = NewObj(esriCarto.LayerFile, esriCarto.ILayerFile)
    pLayerFile.New(outpath)
    pLayerFile.ReplaceContents(pLayer)
    pLayerFile.Save()

    return outpath


def find_layer(servicedesc, layername):
    '''Recursive layer matching.
       Recursion is necessary to handle non data (folder/subfolder) "layers"'''
    layer = None
    for i in range(servicedesc.LayerDescriptionCount):
        layerdesc = servicedesc.LayerDescription(i)
        if layerdesc.LayerDescriptionCount == 0:
            if layerdesc.Name == layername: return layerdesc
            else: continue
        layer = find_layer(layerdesc, layername)
        if layer:break
    return layer

if __name__ == '__main__':
    url = 'http://someserver/ogc/wms?'
    path1 = r'C:\Temp\test1.lyr'
    path2 = r'C:\Temp\test2.lyr'
    path3 = r'C:\Temp\test3.lyr'
    try:
        #All layers
        lyr = CreateWMSGroupLayer(url, path1, visible=False)
        #A couple of layers
        lyr = CreateWMSGroupLayer(url, path2, False, 'lyr0', 'lyr1')
        #A single layer
        lyr = CreateWMSGroupLayer(url, path3, False, 'lyr2')

    except COMError as e:
        print('Unable to add WMS')
Related Question