Update WebMap dictionary for multiple feature layers

899
7
11-04-2019 12:18 PM
ZacharyHart
Occasional Contributor III

There are several threads on here that have gotten me this far, but I've still not found any solid documentation about what needs to be supplied to the JSON dictionary in order to properly update it. Note: I can get this to work when there is only one layer in the feature layer/feature collection. I'm just stuck.

Here is my code so far.

"""
#Import Modules
import copy,json
from arcgis.gis import GIS
from arcgis.mapping import WebMap

#Log into ArcGIS Enterprise
gis = GIS("https://awesomeportalname.com/portal", "DougMcGrave", "a1ntL!FeGr@nd!")
print("Logged in as: " + gis.properties.user.username)


#Retrieve NortheastFieldData web map
webMapSearch = gis.content.search("title: NortheastFieldData2",item_type = "Web Map")
sourceWebMap= webMapSearch[0]
#itemID = webMapTest.id
#print(itemID)
#Read the web map as a WebMap object
webMapObject = WebMap(sourceWebMap)

#Create a copy of the web map with a new title
webmap_item_properties = {'title': 'TestCopyThis', 'snippet':'testcopydis', 'tags':'Test'}
webMapObject.save(webmap_item_properties)

#Retrieve new web map
newWebMapSearch = gis.content.search("title: TestCopyThis",item_type = "Web Map")
newWebMap = newWebMapSearch[0]


for layer in newWebMap.layers:
    print(layer.title)

#Extract JSON from webmap
pubMapJson = newWebMap.get_data(try_json=True)
jsonCopy = copy.deepcopy(pubMapJson)

#Layers in web map which need filter applied
layersList = ['NortheastCruisePoints','NortheastClientsCollector - NortheastPoints','NortheastClientsCollector - NortheastPoints','NortheastClientsCollector - NortheastLines','NortheastClientsCollector - NortheastStands','NortheastClientsCollector - Harvest Operations Status']

#Filter to be applied
lyrFilter = "LVI_CODE = '{0}'".format("7532")
    
#Extract JSON from webmap and create a copy
pubMapJson = newWebMap.get_data(try_json=True)
jsonCopy = copy.deepcopy(pubMapJson)

#Loop through each layer
for lyr in layersList:
        if 'layerDefinition' in lyr:
            print(lyr)
            jsonCopy['operationalLayers'][0]['layerDefinition']['definitionExpression']=lyrFilter
            
#Update JSON
newWebMap.update(item_properties={'text':json.dumps(jsonCopy)})‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

No errors are given and the JSON update returns 'True'

0 Kudos
7 Replies
ZacharyHart
Occasional Contributor III

I modified the the loop a bit and it seemed like it was writing the JSON back, but when viewing the map still no luck. Can anyone help???

for layer in newWebMapObject.layers:
    for lyz in layersList:
        if layer.title == lyz:
            print(layer.title)
            jsonCopy['operationalLayers'][0]['definitionExpression'] = lyrFilter
            newWebMap.update(item_properties={'text':json.dumps(jsonCopy)})
0 Kudos
ZacharyHart
Occasional Contributor III

When I embarked on this quest several weeks back I had no idea it would be so difficult! If it's any testament to this: I've had a ticket open with support services for almost 2 weeks and they've just escalated it. If/when I find an answer I'll be happy to share it back here!

0 Kudos
ZacharyHart
Occasional Contributor III

FWIW, this what the support techs came up with for a solution. It's basically borrowed from several other threads on GeoNet, but i've yet to get an answer about the need for the enumeration. I was able to modify their code to accommodate a loop to handle multiple layers [not included here; i suspect there are a number of redundancies which can be eliminated from my own solution]. 

Esri's code:

from arcgis.gis import GIS
import copy
from json import dumps
from json import JSONDecoder
from arcgis.mapping import WebMap

gis = GIS("https://supt0010856.esri.com/portal", "siteadmin", "siteadmin1")
print("Logged in as: " + gis.properties.user.username)

#Retrieve NortheastFieldData web map
webMapSearch = gis.content.search("title: LynseyTest2",item_type = "Web Map")
sourceWebMap= webMapSearch[0]

webMapObject = WebMap(sourceWebMap)

#Create a copy of the web map with a new title
webmap_item_properties = {'title': 'TestCopyLynsey3', 'snippet':'testcopydis', 'tags':'Test'}
webMapObject.save(webmap_item_properties)

newWebMapSearch = gis.content.search("title: TestCopyLynsey3",item_type = "Web Map")
newWebMap = newWebMapSearch[0]
print (newWebMap)

for layer in WebMap(newWebMap).layers:
    print(layer.title)

#Extract JSON from webmap 
pubMapJson = newWebMap.get_data(try_json=True)
jsonCopy = copy.deepcopy(pubMapJson)
#print copy of webmap json
print (jsonCopy)

lyrFilter = "stringtest = '{0}'".format("test")
print (lyrFilter)

fldMapJsonCopy = copy.deepcopy(pubMapJson)

lyrT = [l for l in pubMapJson['operationalLayers'] if l['title']== 'APITest']
if len(lyrT) >0:
    tZoneLyr = lyrT[0]
    if 'layerDefinition' in tZoneLyr:
       z = [[x, y] for x, y in enumerate(pubMapJson['operationalLayers']) if y['title'] == 'APITest'][0]
       fldMapJsonCopy['operationalLayers'][z[0]]['layerDefinition']['definitionExpression'] = lyrFilter

final= newWebMap.update(item_properties={'text':dumps(fldMapJsonCopy)})
print (final)

‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Any one have any idea what the enumeration is doing here?

0 Kudos
sofoo
by
Occasional Contributor

It looks like you've hard-coded the layer index to [0] so it seems to be only updating the first layer every time you loop?

0 Kudos
AnttiKajanus
New Contributor III

This seems to be a bit old question but I just answered to similar case where layer level properties are added and saved to the webmap (with different property on the layer though) in https://community.esri.com/thread/246654-setting-the-refresh-interval-for-a-hosted-feature-layer#com... 

ZacharyHart
Occasional Contributor III

This is interesting to me because every other thread I've seen regarding this has a process by which you need to extract the JSON from the web map (deepcopy) modify it and then apply the full JSON back to the webmap. Obviously, your method is simpler.

However, I cannot seem to the 'definitionExpression' key by your method. For example:

json_key = 'layerDefinition'
for layer in webMapTest.layers:
    if 'layerDefinition' in layer:
        print(layer.title)
        print(layer['layerDefinition'])

returns the following for each layer:

NortheastClientsCollector - NortheastLines
{'minScale': 80000, 'maxScale': 0, 'definitionExpression': "LVI_CODE = '0'"}

however, if you try the following:

json_key = 'layerDefinition'
for layer in webMapTest.layers:
    if 'layerDefinition' in layer:
        print(layer.title)
        print(layer['definitionExpression'])

you get a key error:

KeyError: 'definitionExpression'

printing webMapTest.definition, I can see that 'definitionExpression' is a child element of 'layerDefinition' :

      "layerDefinition": {
        "definitionExpression": "LVI_CODE = '0'"
      },

I don't know how to modify that child element.

I'm adding another response to this thread for some additional clarity.

0 Kudos
AnttiKajanus
New Contributor III

One way to do that would be doing something along these lines. Haven't tested well I get my layer to change the definition expressions with this.

layer = webmap.layers[1]
print(layer.title)
if "layerDefinition" in layer:
    if "definitionExpression" in layer["layerDefinition"]:
        layer["layerDefinition"]["definitionExpression"]  = "RakennelmanTyyppi <> 2"
        print('definitionExpression updated to value {}'.format(layer["layerDefinition"]["definitionExpression"]))
    else:
        layer["layerDefinition"]["definitionExpression"]  = "RakennelmanTyyppi <> 2"
        print('definitionExpression updated to value {}'.format(layer["layerDefinition"]["definitionExpression"]))
else:
    layer["layerDefinition"] = {
        "definitionExpression": "RakennelmanTyyppi <> 2"
    }
    print('layerDefinition added to value {}'.format(layer["layerDefinition"]))

webmap.update()‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos