ArcGIS API for Python Blog

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Latest Activity

(26 Posts)
MehdiPira1
Esri Contributor

 

Introduction

Smartsheet is a dynamic workspace that empowers teams to manage projects, automate workflows, and rapidly build new solutions. Smartsheet uses spreadsheets, referred to as sheets, as the basis of everything it does, but the difference between Smartsheet and spreadsheet programs like Microsoft Excel or Google Sheets is that Smartsheet has all sorts of collaboration functionality incorporated into it.

The screenshot below is a sample Smartsheet data.

MehdiPira1_0-1636077173116.png

In this blog, we will update a point feature layer in ArcGIS Online (AGOL) with a Smartsheet dataset using ArcGIS API for Python. In the process, first we will convert this Smartsheet into a Pandas Data Frame, remove rows with no coordinates and then update the feature layer. It is notable that the data contains longitude and latitude in X and Y columns.

The following screenshot is the feature layer that has already been published as a hosted feature layer from the Smartsheet into AGOL before and after updates. As such both the hosted feature layer and the Smartsheet have the same schema and fields. This Smartsheet gets updated on a weekly basis. Therefore the hosted feature layer needs to be updated as well to reflect the updates on the web map. Using ArcGIS API for Python, this process can be automated.

ezgif.com-gif-maker.gif

 

Requirements

There are three items that are required before data conversion:

  1. Install Smartsheet library by running this command “pip install smartsheet-python-sdk” in Python Command Prompt (here python is in D drive: “D:\ArcGIS\Server\framework\runtime\ArcGIS\bin\Python\envs\arcgispro-py3\Scripts>”)
  2. Smartsheet Access Token - here’s a link which shows how to create one
  3. Sheet ID – to obtain the sheet ID, in Smartsheet go to File > Properties > Sheet ID

 

Methodology

1. Smartsheet Conversion into a Data Frame

First import the modules.

MehdiPira1_15-1636011751820.png

Input the access token generated from the Smartsheet in the script below. After the Smartsheet authentication, a function is used to implement the Smartsheet to data frame conversion.

MehdiPira1_16-1636011900915.png

Input the sheet ID copied from the properties into the following script, then we call the function to create the data frame.

MehdiPira1_19-1636012319136.png

The rows with no latitudes or longitudes are removed.

MehdiPira1_20-1636012378136.png

MehdiPira1_1-1636078995890.png

Note that X and Y columns are the longitude and latitude coordinates respectively.

 

2. Export Data Frame into a CSV

Once the data frame is created, it is output as a temporary csv file in a folder. Here, a TEMP folder in C drive is used.

MehdiPira1_23-1636012690999.png

 

3. Upload CSV to AGOL

Connect to AGOL, then remove any existing csv item with the same title from AGOL.

MehdiPira1_24-1636012777395.png

When properties are set with title, description, and tags, upload the csv file to the Smartsheet's folder in AGOL. We can skip the description and tags here and can just use the title since this item is temporary and gets deleted when a new csv is added.

MehdiPira1_25-1636012894946.png

 

4. Truncate and Append

After that we get the hosted feature layer by id and we truncate it. This hosted feature layer becomes empty but still retains all the properties and settings.

Finally, we use the Append method to update this feature layer with the newly added csv file.

MehdiPira1_26-1636012995111.png

The good thing about using Append method is that the feature layer’s id does not change. This means that if the feature layer is already used in a web map or a web application, it will not break.

Now the hosted feature layer updated with the Smartsheet is displayed in the web map.

screentshot3.png

There are some points to note:

  • The hosted feature layer has initially been created from the same Smartsheet. Hence the schema, field names and types of both data are the same.
  • Append is currently only available in AGOL and cannot be used in Portal for ArcGIS.
  • I did not talk about the data used here as it can be any data as long as it contains coordinates since the emphasis is predominantly on the methodology.

This script can be used in e.g., Windows Task Scheduler to run on a regular basis for automation of the whole process.

 

Summary

Smartsheet integration with AGOL has been made easier and more efficient using ArcGIS API for Python with just a few lines of code. Therefore, when there are some updates in Smartsheet, these updates will automatically occur in the hosted feature layer whenever the script is run. This ensures the web maps and web applications such as Operations Dashboard display the latest features.

more
10 1 943
AndresCastillo
MVP Regular Contributor

updateWMRenderer&Label

updateWMRenderer&Label.png

 

checkWMLayerOrder

checkWMLayerOrder.png

 

 

configureWMSearch

configureWMSearch_1.png

configureWMSearch_2.png

 

 

updateWMLayerItemID&ConfigurePopup

updateWMLayerItemID&ConfigurePopup.png

     

more
1 6 1,317
AndresCastillo
MVP Regular Contributor

ArcGIS API for python User.items() method says it takes dictionary from the folders property, but the folders property says it returns a list:
 
AndresCastillo_0-1617215088337.png

 

AndresCastillo_1-1617215096675.png

 

 

the folders property actually returns a list of dictionaries:
 
 
AndresCastillo_2-1617215109102.png

more
0 0 391
IhabHassan
Esri Contributor

Recently I came across ESRI web course Update Real-Time Data with Python, and it walks through a tutorial on how to built what is called "feed routine". The idea is basically is to read latest version of certain feature-set from a JSON file shared and accessed through a URL, and feed that into your system, which in this case is a hosted feature layer. 

In this blog, I wanted to focus on the function "deployLogic" which takes places after reading the latest JSON data and converting it to ESRI file-geodatabase. This function demonstrate really important workflow on how to update the data of hosted feature layer, while keeping the hosted layer item id - as a Portal for ArcGIS or AGOL item. I didn't find similar workflow explained in the published documentation for ArcPy or API for Python, and I believe it can be accommodated to be used with more complex scenarios.

I will try to summarize the function logic in the following bullets:

  • Using 7zip sub-process, extract the hosted layer service definition file
  • Browse to the extracted folder, reach the underlying source file gdb, and delete it
  • Copy the fresh file-geodatabase that was created in previous step to the extracted service definition folder
  • Using 7zip, compress the service definition folder into a new service definition file with '.sd' extension, not '.zip'
  • Using "FeatureLayerCollectionManager" instance, update the hosted layer using the new service definition file


The simplicity of this workflow is that it avoids republishing the service using ArcGIS Pro project, so you won't need to go through something like "getWebLayerSharingDraft" from a Map object, and worry about setting the needed flags "overwriteExistingService", etc.. I find this really easy shortcut, and it will get the job done.

Update Real-Time Data with Python

Update Real-Time Data with Python

Update Real-Time Data with Python

more
6 2 1,263
DavidVitale
New Contributor III

Happy Halloween! The ArcGIS API for Python has just released its newest version, 1.7.0. View the release notes here to see what's new and what bugs have been fixed. Visit our landing page for all other information about the Python API.

You can install this newest version via conda:

conda install -c esri arcgis

or pip:

pip install arcgis

We can't wait to hear what you think of this release!

more
4 10 3,665
RobertWeber
New Contributor III

We create tile packages for field workers fairly frequently so they can have additional maps available to them when they go offline in the field.  Typically this process has involved several different tools depending on the particular map or imagery that is needing to be tiled.  Generally, I have found the ManageTileCache and ExportTileCache GP tools to be most reliable.  So recently I started thinking about combining them along with the Python API so we could have one tool to use for publishing TPK's to our AGOL Org. Any feedback or suggestions would be greatly appreciated.

Basic outline of what the tool does:

  1. Make connection and get a token from your portal with whatever credentials are being used in Pro
  2. Take an input raster dataset
  3. Create a new folder for outputs in the same directory as the input dataset
  4. Allow user to name output dataset
  5. Run ManageTileCache tool to create a local tile cache ( by default the entire extent of the input dataset is used and LOD's are created based off of the input data source pixel information)
  6. Run ExportTileCache tool to create local tpk file
  7. Report file size info back via arcpy Messages
  8. Add tpk item to portal
  9. Attempt to publish the tpk as hosted tile layer
    • Publishing a tpk item seemed to need the additional step of making a POST via the REST api to actually unpack and publish the tiles using '/updateTiles' contained in the tpk  this is done using requests.post just simply using publish() with the python api tended to fail at going beyond creating the hosted tile layer.
  10. Uses urllib to get additional info about the hosted tile layer and LOD's being published.

This tool has been handy in certain situations for us and I hope it is for someone else too.  Still I am always interested if someone has a better way of doing this.

more
2 0 1,103
Oliver_Burdekin
Occasional Contributor II

For anyone interested in GIS development and Python I've put together a tutorial on using esri's ArcGIS Python API in Jupyter Notebooks.

ArcGIS Python API in Jupyter Notebooks | burdGIS - YouTube 

#gis #python #esri #arcgis #tutorial #jupyter

more
2 0 649
EarlMedina1
Occasional Contributor III

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!

more
10 6 8,455
RogerDunn
Occasional Contributor II

Our Storm Water personnel maintain several feature classes in an enterprise geodatabase, which is currently at version 10.6.  Each of these feature classes is related to two tables: one for inspections and one for events.  Each feature class has a field which represents the number of days since the feature was visited, either by inspection or event.  They symbolize their layers by this field, using graduated colors, to show how long it's been since the feature was visited.  For example, red features haven't been visited in a long time while green ones have been visited recently.  Storm Water wanted this field updated automatically every night.

I wrote a Python script which performs the update.  I recently extrapolated it to be very general, to apply to any geodatabase layer and any number of related tables.  I call the method once per feature class.  I apologize if the documentation is not in a format to your liking, but I followed some examples I found on-line.  Since it is just a script, you're free to modify it to your whim:

# Script written by Roger Dunn, Information Technology Division, City Manager's Office, City of Orem, Utah
# February 2019
# Requires ArcGIS Desktop Standard or Advanced.
# Written with ArcPy for Desktop 10.6.1
# Target geodatabase is a 10.6 geodatabase affectionately called Knight, but really called OREMEGDB

# Use ArcPy
import arcpy

# DateTime functions needed
import datetime

def UpdateMasterFeatureClassDayRangeFieldFromMostRecentDetailEventTable(\
    arcSDEConnectionFileName \
    , masterFeatureClassName \
    , masterKeyFieldName \
    , masterDayRangeFieldName \
    , detailEventTableNames \
    , detailEventForeignKeyFieldNames \
    , detailEventDateFieldNames \
    , valueIfNoHistoryFound
    ):
    '''
    Updates an ArcGIS geodatabase feature class field which represents the number of days
    since something happened in a detail event table (which may or may not be related via
    a relationship class in ArcGIS).  This method doesn't return a value.

    :param str arcSDEConnectionFileName: The name of an .sde connection file.  If this
    script is running in ArcGIS Desktop, then it suffices to use 'Database Connections\'
    followed by the name of the file.  If this is automated, then the .sde file should
    be in the same directory as this script.
    
    :param str masterFeatureClassName: The name of the feature class to be modified.  The
    connection information in arcSDEConnectionFileName should have the user name and
    password (saved) of a user with permissions to modify masterFeatureClassName.  Note
    that masterFeatureClassName does not include the name of the parent feature dataset.

    :param str masterKeyFieldName: The name of the primary key field in the master feature
    class.

    :param str masterDayRangeFieldName: The name of the field in the master feature class
    which will hold the date range from today's date back to the most recent event in any
    of the detailEventTableNames.

    :param str[] detailEventTableNames: The names of tables related to the master feature
    class.

    :param str[] detailEventForeignKeyFieldNames: For each table listed in detailEventTableNames,
    include the name of the field in that table that corresponds to the master feature class'
    primary key field.  Therefore, this list is as long as detailEventTableNames.

    :param str[] detailEventDateFieldNames: For each table listed in detailEventTableNames,
    include the name of the field in that table that contains the event date.  Therefore, this
    list is as long as detailEventTableNames.

    :param var valueIfNoHistoryFound: The default value to insert in the master feature class'
    DayRangeField if no history can be found in any of the detailEventTableNames for that
    particular feature.
    ...
    '''

    # Change the environment workspace, and allow previous temporary datasets be overridden
    arcpy.env.workspace = arcSDEConnectionFileName
    arcpy.env.overwriteOutput = True
    # Store "today" (don't let it change during the script's iterations
    scriptToday = datetime.date.today()
    # Create a dictionary from the detail tables where the key is the unique ID of the master record
    # and the value is the most recent date for a detail record.
    # Initialize the dictionary
    valueDict = {}
    for detailTableIndex in range(len(detailEventTableNames)):
        detailFields = [\
            detailEventForeignKeyFieldNames[detailTableIndex] \
            , detailEventDateFieldNames[detailTableIndex] \
        ]
        with arcpy.da.SearchCursor(detailEventTableNames[detailTableIndex], detailFields) as readRows:
            for readRow in readRows:
                masterID = readRow[0]
                dateVal = readRow[1]
                if not masterID is None and not dateVal is None:
                    dateOnly = dateVal.date()
                    if not masterID in valueDict:
                        valueDict[masterID] = dateOnly
                    else:
                        if dateOnly > valueDict[masterID]:
                            valueDict[masterID] = dateOnly
                    del dateOnly
                del masterID
                del dateVal
                del readRow
        del detailFields
    del detailTableIndex
    #print valueDict

    # With valueDict now populated, it's time to modify the masterFeatureClass' DayRangeField with
    # what we've found
    
    masterFields = [masterKeyFieldName, masterDayRangeFieldName]
    edit = arcpy.da.Editor(arcSDEConnectionFileName)
    # Edit session is started without an undo/redo stack for versioned data (for second argument,
    # use False for unversioned data)
    edit.startEditing(False, True)
    try:
        with arcpy.da.UpdateCursor(masterFeatureClassName, masterFields) as masterRows:
            for masterRow in masterRows:
                masterID = masterRow[0]
                if masterID in valueDict:
                    daysSince = (scriptToday - valueDict[masterID]).days
                else:
                    daysSince = valueIfNoHistoryFound
                # If the calculated value for the date range doesn't equal what's already in the
                # field, modify it.  We only want delta rows where there's really a change.
                if masterRow[1] != daysSince:
                    # Start an edit operation
                    edit.startOperation()
                    try:
                        masterRow[1] = daysSince
                        masterRows.updateRow(masterRow)
                        #print '{0} {1} updated. Field {2} = {3}'.format(masterKeyField, masterID, masterEditField, daysSince)
                        # Stop the edit operation.
                        edit.stopOperation()
                    except:
                        edit.abortOperation()
                del daysSince
                del masterID
                del masterRow
    finally:
        # Stop the edit session and save the changes
        edit.stopEditing(True)
    del edit
    del valueDict
    del scriptToday
    del masterFields

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

I call the function like this, but I'm not going to give out the real database, feature class, and table names:

from UpdateLayersFromTables import UpdateMasterFeatureClassDayRangeFieldFromMostRecentDetailEventTable

# Update Feature Class 1
UpdateMasterFeatureClassDayRangeFieldFromMostRecentDetailEventTable( \
    'MyConnection.sde' \
    , 'MySQLDB.SchemaOwner.FeatureClass1' \
    , 'UniqueID' \
    , 'DaysSince' \
    , ['MySQLDB.SchemaOwner.Class1Inspections', 'MySQLDB.SchemaOwner.Class1Events'] \
    , ['FeatClass1UniqueID', 'FeatClass1UID'] \
    , ['InspectionDate', 'EventDate'] \
    , 10000 \
    )

# Update Feature Class 2
UpdateMasterFeatureClassDayRangeFieldFromMostRecentDetailEventTable( \
    'MyConnection.sde' \
    , 'MySQLDB.SchemaOwner.FeatureClass2' \
    , 'UID' \
    , 'DaysSince' \
    , ['MySQLDB.SchemaOwner.Class2Inspections', 'MySQLDB.SchemaOwner.Class2Events'] \
    , ['FeatClass2UniqueID', 'FeatClass2UID'] \
    , ['InspectionDate', 'EventDate'] \
    , 10000 \
    )
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

"Roj"

more
3 3 2,516
EarlMedina1
Occasional Contributor III

Let's say you have a Hosted Feature Layer named worldEQ which contains data on Earthquakes that have occurred throughout the world for the last 50 years:

earthquakes

You wish to create a view named worldEQView from this Hosted Feature Layer. To do that, you could use the following snippet:

from arcgis import GIS
from arcgis.features import FeatureLayerCollection
gis = GIS("https://www.arcgis.com", "username","password")

# Search for Source Hosted Feature Layer 
source_search = gis.content.search("world_earthquakes")[0] 
source_flc = FeatureLayerCollection.fromitem(source_search)

# Create View from Source Hosted Feature Layer 
new_view = source_flc.manager.create_view(name="worldEQView")‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

This works out great and your view is created:

viewresult

Let's suppose you next want to use the view to show only earthquakes that occurred before the year 1988. Reviewing the Data tab of the view's Item Details, you see that you can filter by a column year_:

tbl

When you set a View Definition, that definition is defined at the service level. If you quickly set a test definition in the ArcGIS Online/Portal for ArcGIS user interface and take a look at the view service's Service Definition, you'll see the property that needs to be updated is viewDefinitionQuery:

Click on 'View' in the View's Item Details page

servurl

Next, click on the Layer:

layerrest

Click on 'JSON'

jsonrest

Scroll all the way to the bottom to see the 'viewDefinitionQuery' property:

defrest
Note: changing the value of viewDefinitionQuery also updates the related definitionQuery property

To update the viewDefinitionQuery property with the ArcGIS API for Python, you do the following:

# Search for newly created View
view_search = gis.content.search("worldEQView")[0]
view_flc = FeatureLayerCollection.fromitem(view_search)

# The viewDefinitionQuery property appears under layers
view_layer = view_flc.layers[0]

# Define a SQL query to filter out events past 1988
view_def = {"viewDefinitionQuery" : "year_ < 1988"}

# Update the definition to include the view definition query
view_layer.manager.update_definition(view_def)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

You should be able to see this update reflected after refreshing the view Item Details page > Visualization

def

Altogether, the script to create a View from the Hosted Feature Layer and then to set a View Definition is:

from arcgis import GIS
from arcgis.features import FeatureLayerCollection
gis = GIS("https://www.arcgis.com", "username","password")

# Search for Source Hosted Feature Layer
source_search = gis.content.search("world_earthquakes")[0]
source_flc = FeatureLayerCollection.fromitem(source_search)

# Create View from Source Hosted Feature Layer
new_view = source_flc.manager.create_view(name="worldEQView")

# Search for newly created View
view_search = gis.content.search("worldEQView")[0]
view_flc = FeatureLayerCollection.fromitem(view_search)

# The viewDefinitionQuery property appears under layers
view_layer = view_flc.layers[0]

# Define a SQL query to filter out events past 1988
view_def = {"viewDefinitionQuery" : "year_ < 1988"}

# Update the definition to include the view definition query
view_layer.manager.update_definition(view_def)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

This can be generalized into a standalone script like this one:

import sys
from arcgis import GIS
from arcgis.features import FeatureLayerCollection

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 create_view(conn, source_flc, view_name, layer_index, view_def):
    new_view = source_flc.manager.create_view(name=view_name)
    # Search for newly created View
    view_flc = search_layer(conn, view_name)
    # The viewDefinitionQuery property appears under layers
    view_layer = view_flc.layers[layer_index]
    # Update the definition to include the view definition query
    view_layer.manager.update_definition(view_def)
    print("View created")

def main():
    conn = GIS("https://www.arcgis.com", 
               "username", "password")
    # Index of the Layer to be filtered
    layer_index = 0
    # Define a SQL query to filter out events past 1988
    view_def = {"viewDefinitionQuery" : "year_ < 1988"}
    # Search for target Hosted Feature Layer
    source_flc = search_layer(conn, "world_earthquakes")
    # Create View from Hosted Feature Layer
    create_view(conn, source_flc, "worldEQView", layer_index, view_def)

if __name__ == '__main__':
    sys.exit(main())‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

If you need to define an area of interest, this would be approached like so:

view_def = {"viewLayerDefinition":{"filter":   
{"operator":"esriSpatialRelIntersects","value":
{"geometryType":"esriGeometryEnvelope","geometry":
{"xmin":4485937.7074932605,"ymin":1543545.165101517,
"xmax":9417043.276225261,"ymax":6239836.182941515,
"spatialReference":{"wkid":102100,"latestWkid":3857}}}}}}
view_layer.manager.update_definition(update_dict)

more
11 15 5,473
121 Subscribers
Labels