Quite often, people wonder how to use the ArcGIS API for Python to update layer symbology. After a quick search through posts and documentation, one may discover that there are a number of ways to accomplish this and find oneself uncertain of how to proceed. If you land on this page my hope is that everything will start to make a bit more sense. In this post I'll go over:
- How to update layer symbology in a Web Map
- How to update layer symbology on a layer item
- How to update symbology on a Feature Service Layer
If the above all seem the same don't worry - presently, the distinctions between #1, #2, and #3 will become clear and you will get a better sense of when you might want to choose one method over another.
Note: Although I'm illustrating how to update symbology, you can apply the same concepts to update/configure other properties such as pop-ups, labels, etc. I encourage you to build upon the below standalone scripts or use only the parts you need!
How to Update Layer Symbology in a Web Map:
Let's suppose you add this Map Service to your content in ArcGIS Online/Portal for ArcGIS: https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer
You add the item to a Web Map and for a little while it suits your needs:
![initial_webmap](https://community.esri.com/legacyfs/online/441506_WebMap_initial.PNG)
But one day you decide that you don't really like the color orange and would get more use out of the Web Map if you could better visualize total population of each county.
After some time, you settle on the symbology included in the attached webmaplyr.json. It's a bit a long so I won't include it here - this is fine because you probably won't want to define the JSON in your script anyway, preferring to read from a file.
Note:
If you don't know where to get started as far as generating your own JSON don't fret. Simply start by creating a Web Map exactly as you want it to appear (apply styles, labels, options, etc.). From there, you can go to (where itemID is the item id of the Web Map):
Alternatively, you can get the JSON using the ArcGIS API for Python with a few lines:
from arcgis import GIS
import json
conn = GIS("https://machine.domain.com/portal", "admin", "password")
item = conn.content.get(<itemID>)
item_data = item.get_data()
print(json.dumps(item_data, indent=4, sort_keys=True))
The below script illustrates how to read your prepared JSON from a file and apply it to the Web Map:
from arcgis import GIS
import json, sys
def search_item(conn,layer_name):
search_results = conn.content.search(layer_name, item_type='Web Map')
proper_index = [i for i, s in enumerate(search_results) if
'"'+layer_name+'"' in str(s)]
found_item = search_results[proper_index[0]]
get_item = conn.content.get(found_item.id)
return get_item
def update_wm_layerdef(item):
item_data = item.get_data()
print("*******************ORIGINAL DEFINITION*********************")
print(json.dumps(item_data, indent=4, sort_keys=True))
with open('/path/to/webmaplyr.json') as json_data:
data = json.load(json_data)
item_properties = {"text": json.dumps(data)}
item.update(item_properties=item_properties)
new_item_data = item.get_data()
print("***********************NEW DEFINITION**********************")
print(json.dumps(new_item_data, indent=4, sort_keys=True))
def main():
conn = GIS("https://machine.domain.com/portal",
"admin", "password")
item = search_item(conn, 'wm_lyrsym')
update_wm_layerdef(item)
if __name__ == '__main__':
sys.exit(main())
After the script runs, you end up with the below:
![updated web map](https://community.esri.com/legacyfs/online/441507_WebMap_update.PNG)
How to Update Portal/ArcGIS Online Item Symbology:
The difference here is subtle. In Option #1, the item being updated is a Web Map. Here, the item being updated is a Feature/Map Image Service Layer. The service might be something you published to ArcGIS Online/Portal for ArcGIS, or a Map/Feature Service you added as an item to your content.
Option #1 is great if all you need to do is change the styles in a Web Map, but perhaps you need to change the style for an item in your Organization. Since this item is used by many people (and deliverables are occasionally provided to stakeholders outside of the organization) you wish to standardize its appearance.
In this example, I use the same Map Service as before (https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer) and perform a similar update to the symbology, only on the Layer item itself.
Note that this example is a bit more specific on how the symbology is updated:
layer_def = item_data['layers'][3]['layerDefinition']
- Here you see that only the layer at index 3 is updated (counties) - everything else in the service is left alone.
- Review drawingInfo.json and you can see that the renderer is updated from Single symbol to Classified.
You might wonder why this is included:
else:
print("There is no Layer Definition at the moment..creating one...")
create_layer_def(item)
- A layer definition isn't necessarily created by default. Let's suppose I only just added that Map Service as an item to my content - that ArcGIS Online/Portal for ArcGIS item has no layer definition attached it.
- If added the item to a Web Map, made a simple symbology change, and saved the layer this would generate an layer definition.
- complete.json contains the layer definitions that would apply to the entire item (all 4 layers).
from arcgis import GIS
import json, sys
def search_layer(conn,layer_name):
search_results = conn.content.search(layer_name, item_type='*')
proper_index = [i for i, s in enumerate(search_results) if
'"'+layer_name+'"' in str(s)]
found_item = search_results[proper_index[0]]
get_item = conn.content.get(found_item.id)
return get_item
def update_layer_def(item):
item_data = item.get_data()
if item_data is not None:
layer_def = item_data['layers'][3]['layerDefinition']
print("*******************ORIGINAL DEFINITION*********************")
print(json.dumps(item_data, indent=4, sort_keys=True))
with open('/path/to/drawingInfo.json') as json_data:
data = json.load(json_data)
layer_def['drawingInfo'] = data
item_properties = {"text": json.dumps(item_data)}
item.update(item_properties=item_properties)
new_item_data = item.get_data()
print("***********************NEW DEFINITION**********************")
print(json.dumps(new_item_data, indent=4, sort_keys=True))
else:
print("There is no Layer Definition at the moment..creating one...")
create_layer_def(item)
def create_layer_def(item):
with open('/path/to/complete.json') as json_data:
data = json.load(json_data)
item_properties = {"text": json.dumps(data)}
item.update(item_properties=item_properties)
item_data = item.get_data()
print("*********************CREATED DEFINITION************************")
print(json.dumps(item_data, indent=4, sort_keys=True))
def main():
conn = GIS("https://machine.domain.com/portal",
"admin", "password")
item = search_layer(conn, 'earl_api_usalyraw')
update_layer_def(item)
if __name__ == '__main__':
sys.exit(main())
How to Update Feature Service Symbology:
In this last example, I illustrate how to update symbology on a Feature Service Layer. Behind the scenes, this method really just performs this operation: Update Definition (Feature Layer)—ArcGIS REST API: Services Directory | ArcGIS for Developers
So, what's the difference here and when would you want to use this approach?
- This will only work for Feature Services.
- You would want to use this approach when you want to make high-level updates to your Feature Service Layers. This can be used to standardize the appearance of Feature Service Layers across the board - without making any client-side modifications, someone adding the Feature Service to a ArcGIS Online/Portal for ArcGIS Web Map would see the same thing as a developer consuming the service in an application.
For this example, I published a Hosted Feature Service containing 2 layers:
- US States/Canada Provinces
- North America Major Railroads
The original Feature Service looks like this:
![fs-original](https://community.esri.com/legacyfs/online/441549_fs-original.PNG)
The JSON for this example isn't very long. I just make a few changes to color and width on the States/Provinces layer:
{
"drawingInfo": {
"renderer": {
"type": "simple",
"symbol": {
"type": "esriSFS",
"style": "esriSFSSolid",
"color": [
202,
46,
204,
105
],
"outline": {
"type": "esriSLS",
"style": "esriSLSSolid",
"color": [
10,
10,
210,
55
],
"width": 0.5
}
}
},
"scaleSymbols": true,
"transparency": 0,
"labelingInfo": null
}
}
In this scenario, since we're updating a Feature Service and not an item we need to use arcgis.features.
from arcgis import GIS
from arcgis.features import FeatureLayerCollection
import json, sys
def search_layer(conn,layer_name):
search_results = conn.content.search(layer_name, item_type='Feature Layer')
proper_index = [i for i, s in enumerate(search_results) if
'"'+layer_name+'"' in str(s)]
found_item = search_results[proper_index[0]]
flc = FeatureLayerCollection.fromitem(found_item)
return flc
def update_layer_def(layer):
with open('/path/to/hosted_drawinfo_lyr.json') as json_data:
data = json.load(json_data)
layer.manager.update_definition(data)
print("*******************UPDATED DEFINITION**********************")
print(layer.properties)
def main():
conn = GIS("https://machine.domain.com/portal",
"admin", "password")
flc = search_layer(conn, 'layerdef')
layer = flc.layers[1]
print(layer.properties)
update_layer_def(layer)
if __name__ == '__main__':
sys.exit(main())
- The States/Provinces layer was at index 1.
The update result:
![fs-update](https://community.esri.com/legacyfs/online/441584_fs_update.PNG)
That concludes this overview on how to update layer symbology. Hopefully with this guide you can get a good sense of how to implement similar workflows in your organization!