Methods for Updating Layer Symbology with the ArcGIS API for Python

14900
14
04-09-2019 09:25 AM
EarlMedina
Esri Regular Contributor
14 14 14.9K

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:

  1. How to update layer symbology in a Web Map
  2. How to update layer symbology on a layer item
  3. 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

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()
# Include the below line for prettified JSON
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))
    # Open JSON file containing symbology update
    with open('/path/to/webmaplyr.json') as json_data:
        data = json.load(json_data)

    # Set the item_properties to include the desired update
    item_properties = {"text": json.dumps(data)}

    # 'Commit' the updates to the Item
    item.update(item_properties=item_properties)

    # Print item_data to see that changes are reflected
    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")
    
    # Search for item, get item data)
    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

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:
        # Here note we are changing a specific part of the Layer Definition
        layer_def = item_data['layers'][3]['layerDefinition']
        print("*******************ORIGINAL DEFINITION*********************")
        print(json.dumps(item_data, indent=4, sort_keys=True))

        # Open JSON file containing symbology update
        with open('/path/to/drawingInfo.json') as json_data:
            data = json.load(json_data)

        # Set the drawingInfo equal to what is in JSON file
        layer_def['drawingInfo'] = data

        # Set the item_properties to include the desired update
        item_properties = {"text": json.dumps(item_data)}

        # 'Commit' the updates to the Item
        item.update(item_properties=item_properties)

        # Print item_data to see that changes are reflected
        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)

    # Set the item_properties to include the desired update
    item_properties = {"text": json.dumps(data)}

    # 'Commit' the updates to the Item
    item.update(item_properties=item_properties)

    # Print item_data to see that changes are reflected
    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")
    
    # Search for item, get item data)
    item = search_layer(conn, 'earl_api_usalyraw')
    # Attempt to update Layer Definition
    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:

  1. US States/Canada Provinces
  2. North America Major Railroads

The original Feature Service looks like this:

fs-original

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):
    # Open JSON file containing symbology update
    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")
    
    # Search for item, get item data)
    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

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!

14 Comments
ClayDonaldsonSWCA
Occasional Contributor II

Thanks for this write up. I have a methodology that allows me to use one editing template in collector, but shows many symbols.

I have been looking for a way to automate, this helps a lot!

PeterMilenkovic
Occasional Contributor

Thanks for the write up, managed to get this working in one of my scripts. I have a web map that contains a feature layer that is getting updated each day (daily cumulative counts). Now i use the data in the feature layer to generate new class max values so that the symbology keeps up to date with the layer.

by Anonymous User
Not applicable

This is definitely a helpful script for me to update the symbology of classified hosted feature layers. Thanks for sharing it. I have a question for you. I went through the example that you have for map image layers. I have a map image layer with arrow symbols and the arrows are in the middle of the line. This symbology is defined in ArcGIS Pro and is considered as complex or advanced symbology. When I am checking its JSON file in ArcGIS Enterprise with Python API, I see that the style of symbology is "esriSLSSolid". There is no property in the JSON file about the arrows. How can I update the class maximum values of this layer and keep the arrows? 

This is a screenshot of the map image layer. 

Map image layer with classification and arrow symbols

Thank you

by Anonymous User
Not applicable

Fascinating, thank you for sharing 

LanceKirby2
Occasional Contributor II

Does this work for ArcGIS Enterprise referenced feature layers?

AndresCastillo
MVP Regular Contributor

Nice post!

Here are some additional snippets to manage a webmap, including updating the web map layer symbology, label, item id, popup configuration, search configuration, and checking if the web maps have the expected layer.

https://community.esri.com/t5/arcgis-api-for-python-blog/snippets-to-manage-a-web-map-with-the-arcgi...

MichaelLev
Occasional Contributor III

@EarlMedina and @AndresCastillo , Thanks for the info.

Am I understanding well that also from within the Javascript of a WebAppbuilder application, I can modify the symbology of some specific layer?

If the answer is positive, I will appreciate it if I get some links showing how to start.

Clubdebambos
Occasional Contributor III

Excellent, I have used parts of this code many times to automate and standardise symbology across projects areas and automate updates of such as needed #efficiency

ClayGrant
New Contributor

Does this still work?  I am having problem updating a FeatureLayer Service.

David_Brooks
MVP Regular Contributor

@EarlMedina does option 2 above work for Imagery Layers?

When publishing and imagery service direct from ArcGIS Pro Catalog window you can't apply a custom symbology and stretch type until after the service is published in a web map. We would like the option to apply the symbology to the service from an existing styled raster in ArcGIS Pro (like apply symbology geoprocess).

EarlMedina
Esri Regular Contributor

@David_Brooks 

I've not tried it with Imagery Layers, but I imagine something similar should be possible. 

udhayachandran
New Contributor

Title: Excel to ArcGIS Symbology Mapper

Description: The provided Python script facilitates the process of updating ArcGIS feature class symbology based on RGB values extracted from an Excel spreadsheet. The script interacts with ArcGIS and leverages the pandas library to read Excel data. It enables users to specify paths for an Excel file, a feature class, an ArcGIS layer file (.lyrx), and a path for exporting symbology (.lyrx).

My Code:

import pandas as pd
from arcgis.features import FeatureLayer, Feature

def main():
    # Get user inputs
    excel_file = input("Enter the path to the Excel file: ")
    feature_class_path = input("Enter the path to the feature class: ")
    layer_file_path = input("Enter the path to the layer file (.lyrx): ")
    symbology_export_path = input("Enter the path to export the symbology (.lyrx): ")

    # Read RGB values from Excel
    df = pd.read_excel(excel_file)
    mapcode_to_rgb = {}
    for index, row in df.iterrows():
        mapcode = row['MapCode']
        rgb = (row['R'], row['G'], row['B'])
        mapcode_to_rgb[mapcode] = rgb

    # Update values
    feature_layer = FeatureLayer(feature_class_path)
    for feature in feature_layer.query():
        mapcode = feature.attributes['MapCode']
        if mapcode in mapcode_to_rgb:
            rgb = ','.join(map(str, mapcode_to_rgb[mapcode]))
            feature.attributes['RGB'] = rgb
            feature_layer.edit_features(updates=[feature])

    # Create color map
    color_map = {}
    for mapcode, rgb in mapcode_to_rgb.items():
        color_map[mapcode] = {'RGB': ','.join(map(str, rgb))}

    # Apply color map to feature class symbology
    layer = FeatureLayer(feature_class_path)
    layer.update_definition({"renderer": {"type": "uniqueValue",
                                          "field1": "MapCode",
                                          "uniqueValueInfos": list(color_map.values())}})

    # Save the modified symbology to a new .lyrx file
    layer.save(layer_file_path)

    print("Symbology applied and exported successfully!")

if __name__ == "__main__":
    main()
 
Issue is that when i tried to run the script am facing this error 

Enter the path to the Excel file: D:\Development\Python\Working\RGB Symbology\RGB.xlsx
Enter the path to the feature class: D:\Development\Python\Working\Geology_1914A.gdb\GeologyA
Enter the path to the layer file (.lyrx): D:\Development\Python\Working\symbology.lyrx
Enter the path to export the symbology (.lyrx): D:\Development\Python\Working\symbology.lyrx
Traceback (most recent call last):
File "C:\ProgramData\anaconda3\envs\arcgis_env\lib\site-packages\arcgis\gis\__init__.py", line 16940, in _hydrate
self._refresh()
File "C:\ProgramData\anaconda3\envs\arcgis_env\lib\site-packages\arcgis\gis\__init__.py", line 16914, in _refresh
raise e
File "C:\ProgramData\anaconda3\envs\arcgis_env\lib\site-packages\arcgis\gis\__init__.py", line 16903, in _refresh
dictdata = self._con.post(
File "C:\ProgramData\anaconda3\envs\arcgis_env\lib\site-packages\arcgis\gis\_impl\_con\_connection.py", line 1557, in post
return self._handle_response(
File "C:\ProgramData\anaconda3\envs\arcgis_env\lib\site-packages\arcgis\gis\_impl\_con\_connection.py", line 1027, in _handle_response
self._handle_json_error(data["error"], errorcode)
File "C:\ProgramData\anaconda3\envs\arcgis_env\lib\site-packages\arcgis\gis\_impl\_con\_connection.py", line 1052, in _handle_json_error
raise Exception(errormessage)
Exception: Invalid URL
(Error Code: 400)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "c:\ProgramData\anaconda3\envs\arcgis_env\Symbology.py", line 45, in <module>
main()
File "c:\ProgramData\anaconda3\envs\arcgis_env\Symbology.py", line 21, in main
for feature in feature_layer.query():
File "C:\ProgramData\anaconda3\envs\arcgis_env\lib\site-packages\arcgis\features\layer.py", line 2217, in query
if "objectIdField" in self.properties:
File "C:\ProgramData\anaconda3\envs\arcgis_env\lib\site-packages\arcgis\gis\__init__.py", line 16926, in properties
self._hydrate()
File "C:\ProgramData\anaconda3\envs\arcgis_env\lib\site-packages\arcgis\gis\__init__.py", line 16963, in _hydrate
self._refresh()
File "C:\ProgramData\anaconda3\envs\arcgis_env\lib\site-packages\arcgis\gis\__init__.py", line 16914, in _refresh
raise e
File "C:\ProgramData\anaconda3\envs\arcgis_env\lib\site-packages\arcgis\gis\__init__.py", line 16903, in _refresh
dictdata = self._con.post(
File "C:\ProgramData\anaconda3\envs\arcgis_env\lib\site-packages\arcgis\gis\_impl\_con\_connection.py", line 1557, in post
return self._handle_response(
File "C:\ProgramData\anaconda3\envs\arcgis_env\lib\site-packages\arcgis\gis\_impl\_con\_connection.py", line 1027, in _handle_response
self._handle_json_error(data["error"], errorcode)
File "C:\ProgramData\anaconda3\envs\arcgis_env\lib\site-packages\arcgis\gis\_impl\_con\_connection.py", line 1052, in _handle_json_error
raise Exception(errormessage)
Exception: Invalid URL
(Error Code: 400)

 

Am I missing anything? Expecting a good response much appreciable. Thanks In advance

Clubdebambos
Occasional Contributor III

This doesn't seem to work on Hosted Feature Services in ArcGIS Online anymore. I have been attempting to update symbology for a couple of days using the API versions 2.1.0.2 and 2.2.0.1 and it simply does not take. It seems to coincide with updating the Visualization Tab to the Newer Map Viewer over the classic, but this could just be coincidence. Even if I change the symbology manually in ArcGIS Online, the change is not reflected in the properties returned by the API for a FeatureLayer. If I add a filter in the Visualization tab and save, the filter is also not returned by the API. However, if I open up AGO Assistant I can see the correct JSON. What's going on?

ShreyesShiv1
New Contributor II

What you have described here is really helpful.

I have another problem where I have the symbology defined in ArcGIS Pro. How do i apply that symbology to a layer in the web map using the (1. How to update layer symbology in a Web Map) methodology