Using the ArcGIS API for Python to create a view from a Hosted Feature Layer and to define a view definition

9734
20
02-11-2019 03:23 PM
EarlMedina
Esri Regular Contributor
12 20 9,734

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)
20 Comments
RichardLi3
New Contributor II

Thanks  Earl Medina.

BTW, does the last step ( refreshing the view Item Details page > Visualization) has to be done manually? API should be able to do it. What is it?

EarlMedina
Esri Regular Contributor

Hey Richard,

You don't really have to do that step.  It's just included for illustrative purposes.

-Earl

RichardLi3
New Contributor II

Thanks Earl.

New to API, here is another question:

What if I want to update the view's Filter in Item Detail > Visualization >Filter as the same definitionQuery? Does API update_definition support doing so?

Thanks,

Richard 


EarlMedina
Esri Regular Contributor

Hi Richard,

I'm afraid I don't quite understand your question. Can you explain further?

RichardLi3
New Contributor II

Hi Earl,

I'm trying to use API to create featurelayer views for each city based on a province feature layer, for example set the view's "viewDefinitionQuery" as " AOIname = 'Toronto' " using following code. 

# Define a SQL query to filter

update_dict = {"viewDefinitionQuery" : "AOIname = 'Toronto'"}
# Update the definition to include the view definition query
service_layer.manager.update_definition(update_dict)
Ideally, the new_created view should have a spatial extent as the city's boundary. However, After running above code, if you check the item detail > Visualization > Filter (as in following picture), you will found that the Filter in Visualization is empty. To make viewer extent zoom to city's boundary, you have to MANUALLY create a same definition query like AOIname = 'Toronto' , then click "Apply Filter and ZoomTo" .

I just want to know, does API support to set this spatial extent "Filter" definition query?  It is a similar function like "Recalculate Feature Class Extent" tool in ArcGIS Pro.
 
Thanks,
Richard
EarlMedina
Esri Regular Contributor

Hi Richard,

I'm pretty sure what you're asking about is described in Egge-Jan Pollé‌ 's post here: https://community.esri.com/people/EPolle_TensingInternational/blog/2019/03/27/create-view-from-hoste...

For a more general explanation of how to accomplish this see this post: https://community.esri.com/thread/230204-arcgis-api-for-python-set-an-area-of-interest-on-a-hosted-f...

Kind regards,

Earl

RichardLi3
New Contributor II

Thanks Earl. 

Egge-Jan_Pollé
MVP Regular Contributor

Hi Richard,

Yes, Earl Medina‌ is right: I had exactly the same challenge as you have. I had the geometry of - in my case 12 - provinces and I wanted to create 12 views using the provincial boundaries as 'Area of Interest'.

Please let us know whether you managed to create the views for Toronto and the other cities using the script in my blog: 

https://community.esri.com/people/EPolle_TensingInternational/blog/2019/03/27/create-view-from-hoste... 

Cheers,

Egge-Jan

RichardLi3
New Contributor II

Read your article then, it works for my case.

Thanks Egge-Jan.

Richard

Egge-Jan_Pollé
MVP Regular Contributor

You might consider to 'like' the blog post 🙂

Raul_Jimenez
Esri Contributor

Thanks Earl Medina for this awesome post .

I'm trying to accomplish what you have explained here but directly through the REST API, I posted a question last Thursday (Join Features and save result as Layer view through the REST API) but I didn't get any response yet ;(.

Could you help me with it? Or would it be better to talk to someone from Shannon Kalisky‌'s team?

Best regards!,

Raul

Hung_ChunWang
New Contributor

Hi, Earl Medina

Thanks for sharing the info. 

Just wondering do you happen to know what would be the way if l would like to overwrite layers via Python for those that have view layer created.

I have developed the code that can successfully overwrite layers without view layer and trying to figure out how to solve the issue mentioned above.

Thanks

Careinvest
New Contributor

Thanks, can you please advise how to use the Python API to create a view from a "Table (hosted, view)"?

I want to create a view like you do but the type property of my source is 'Feature Service' (it shows as Table (hosted, view) in the portal) and I get a KeyError: 'spatialReference'. 

I have therefore changed the code to 

from arcgis.features import Table

#and

source_table = Table.fromitem(source)

#and

view = source_table.manager.create_view(name='Test_view')

However, this returns 

AttributeError: 'FeatureLayerManager' object has no attribute 'create_view'

Can you please advise how to use the Python API to create a view from a "Table (hosted, view)"?

MehdiPira1
Esri Contributor

Nice one @EarlMedina !

PeterGoedbloed1
New Contributor III

Is it possible that this no longer works since the way views are edited and updated has changed? I can't get this working:

a piece of code like this:

view_def = {"viewDefinitionQuery" : "werksoort = 170"}
print(view_def)

view_layer.manager.update_definition(view_def)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

returns:

SyntaxError: invalid character in identifier 

 

Clubdebambos
Occasional Contributor III

This is fantastic and has great potential for implementation where data can be stored in a master feature service and provided to end users as a regional/localised view.

RamonWilliams
New Contributor III

Good Morning All,

When creating views using the script above, is there anyway to create the view based off one feature class layer and then use and extent to zoom to spatially from another layer?

 

for example my views as based off a point layer but in the table it does not define the states or counties the points are in.  I want it to spatially point to VA to get all the points and i'm having trouble doing it.

 

Thanks in advance.

Clubdebambos
Occasional Contributor III

Hi @RamonWilliams 

I have done similar based on the content of this blog, you get the extent info for the feature of interest in another layer, more than likely a polygon, and in your case the state of VA. The below uses a polygon boundary from a layer to create a view from a point layer. 

from arcgis import GIS
from arcgis.features import FeatureLayerCollection

## connect to AGOL
agol = GIS("home")

## get the feature service you want to create a view from
item = agol.content.get("FS_ID")

## turn item to a FeatureLayerCollection object
flc = FeatureLayerCollection.fromitem(item)

## get boundary of area of interest from another feature service
aoi_record = agol.content.get("OTHER_FS_ID").layers[0].query(where="STATE_NAME='VA'")

## get srs of aoi
srs = aoi_record.spatial_reference

## get geometry of aoi boundary
view_geom = aoi_record.features[0].geometry.get('rings')

## create view
view = flc.manager.create_view(name="OUTPUT_VIEW_NAME")

## get layer to apply the extent filter to
lyr = view.layers[0]

## dictionary with extent filter details
update_dict = {
    "viewLayerDefinition" : {
        "filter" : {
            "operator" : "esriSpatialRelContains",
            "value" : {
                "geometryType" : "esriGeometryPolygon",
                "geometry" : {
                    "rings": view_geom,
                    "spatialReference":srs
                }
            }
        }
    }
}

## apply extent filter
print(lyr.manager.update_definition(update_dict))

 

RamonWilliams
New Contributor III

Thanks I am going to try this.  I appreciate it.

RamonWilliams
New Contributor III

@Clubdebambosthe code work almost perfect, since the rings for my county layer is a census file.  i think the geometry is like nationwide it didnt zoom to VA even though I defined it here:

counties = gis.content.get("XXXXXXXXX").layers[0].query(where="STATEFP='51'")

 

STATEFP='51' is VA fips

 

Do I have to do something the the .geometry.get('ring') like put in all the x,y coordinates to point to VA?

view_geom = counties.features[0].geometry.get('rings')