Skip to main content

Hi,

I am trying to produce a list of all layers that are in Esri webmap. I have the private url of the webmap and I found this Python code

from arcgis.gis import GIS
from arcgis.mapping import WebMap
gis = GIS('home')# if running this from Notebook for ArcGIS in AGOL/Enterprise, replace this line with gis = GIS('home')
wmItemId = "myid" #put the id of the webmap in here
wmItem = gis.content.get(wmItemId)
wm = WebMap(wmItem)
for lyr in wm.layers:
    print(lyr.title)
    print(lyr.url)

Could some please tell me how to use this code in FME?

This works for me in ArcGIS Online, you need to replace <UserName>, <Password> and <ItemId>. Put it in a PythonCaller and it will return a feature with a list called "layer_names".

import fme
import fmeobjects
from arcgis.gis import GIS
from arcgis.mapping import WebMap
 
class FeatureProcessor(object):
 
    def __init__(self):
        pass
 
    def has_support_for(self, support_type: int):
        return support_type == fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM
 
    def input(self, feature: fmeobjects.FMEFeature):
 
        # Connect to your ArcGIS Online organization or Portal for ArcGIS
        gis = GIS("https://www.arcgis.com", "<UserName>", "<Password>")
 
        # Get the web map item by its ID
        web_map_id = "<ItemId>"
        web_map_item = gis.content.get(web_map_id)
        web_map = WebMap(web_map_item)
 
        # Create an empty list to store layer names
        layer_names = []
 
        # Append each layer name to the list
        for layer in web_map.layers:
            layer_names.append(layer.title)
        
        # Create a feature with the list of layer names
        feature_out = fmeobjects.FMEFeature()
        feature_out.setAttribute("layer_names", layer_names)
        self.pyoutput(feature_out)
 
    def close(self):
        pass
 
    def process_group(self):
        pass

 


Hi, I am getting an error

 

2024-04-08 14:26:28|  11.6|  4.6|ERROR |Python Exception <Exception>: A general error occurred: Could not login. Please ensure you have valid credentials and set your security login question.
2024-04-08 14:26:28|  11.6|  0.0|ERROR |Traceback (most recent call last):
2024-04-08 14:26:28|      |     |ERROR |  File "C:\Program Files\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\lib\site-packages\arcgis\auth\_auth\_token.py", line 841, in token
2024-04-08 14:26:28|      |     |ERROR |    self._init_response_type_token()
2024-04-08 14:26:28|      |     |ERROR |  File "C:\Program Files\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\lib\site-packages\arcgis\auth\_auth\_token.py", line 540, in _init_response_type_token
2024-04-08 14:26:28|      |     |ERROR |    raise Exception("Unable to generate oauth token")
2024-04-08 14:26:28|      |     |ERROR |Exception: Unable to generate oauth token
 


  


I have opened fresh FME 2023.0 workbend and have only Creator and PythonCaller with:

  • Esri ArcGIS Python 3.7+
  • and following script
import fme
import fmeobjects
from arcgis.gis import GIS
from arcgis.mapping import WebMap

class FeatureProcessor(object):

def __init__(self):
pass

def has_support_for(self, support_type: int):
return support_type == fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM

def input(self, feature: fmeobjects.FMEFeature):

# Connect to your ArcGIS Online organization or Portal for ArcGIS
gis = ("https://maps-eu.ramboll.com/portal", "USERNAME", "PASSWORD")

# Get the web map item by its ID
web_map_id = "ITEMID"
web_map_item = gis.content.get(web_map_id)
web_map = WebMap(web_map_item)

# Create an empty list to store layer names
layer_names = []

# Append each layer name to the list
for layer in web_map.layers:
layer_names.append(layer.title)

# Create a feature with the list of layer names
feature_out = fmeobjects.FMEFeature()
feature_out.setAttribute("layer_names", layer_names)
self.pyoutput(feature_out)

def close(self):
pass

def process_group(self):
pass

Now for a change I get following error

2024-04-12 14:23:00| 8.3| 0.0|ERROR |Python Exception <AttributeError>: 'tuple' object has no attribute 'content'2024-04-12 14:23:00| 8.3| 0.0|ERROR |Traceback (most recent call last):2024-04-12 14:23:00| | |ERROR | File "<string>", line 22, in input2024-04-12 14:23:00| | |ERROR |AttributeError: 'tuple' object has no attribute 'content'2024-04-12 14:23:00| 8.3| 0.0|ERROR |Error encountered while calling method `input'2024-04-12 14:23:00| 8.3| 0.0|FATAL |PythonCaller_7 (PythonFactory): PythonFactory failed to process feature

I trully do not understand what is going on. In general I need to get a list of all layers that are included in the webmap in Enterpise. I use SingleSingOn to log in to Esri Portal. I would appreciate help as noone seem to know the answer. I thouhg that maybe I can use Esri ArcGIS Online Connector to get this information but I apart from getting private_url of the webmap I cannot extract the information about the content of the webmap


A different way to do this is to use the ArcGISOnlineConnector to find the ItemId of the webmap, then use that ID to get the JSON of the WebMap.

Portal:

https://YourPortalUrl/portal/sharing/rest/content/items/YourItemId/data?f=json

ArcGIS Online:

https://www.arcgis.com/sharing/rest/content/items/YourItemId/data?f=json

Then put the json through a JSONFragmenter. Query is

json["operationalLayers"][*]

and set Flattening = Yes. Then expose what you need (id, title, url) using an AttributeExposer.


This is particularly relevant after the June 25th AGOL Upgrade. Where I used to select the layers and copy/paste from the Web Map Overview page into a spreadsheet for taking notes. That is no longer easy. And good luck getting a list of layers from a web map any other way. Community Forum wins again! :D


Because layers can be nested into groups etc. I use this function to loop to all the layers to get the urls:

 

def find_key(data, target_key, path="json"):
results = []

if isinstance(data, dict):
if target_key in data:
url = data[target_key]
item_id = data.get('itemId', None)
results.append((path, url, item_id))
for key, value in data.items():
new_path = f'{path}["{key}"]'
if isinstance(value, dict) or isinstance(value, list):
results.extend(find_key(value, target_key, new_path))
elif isinstance(data, list):
for index, item in enumerate(data):
new_path = f'{path}[{index}]'
if isinstance(item, dict) or isinstance(item, list):
results.extend(find_key(item, target_key, new_path))

return results

jsonKey = "url"
url_results = find_key(data, jsonKey)

for path, url, item_id in url_results:
end_of_string = None
if url is None:
match = False
print("url is Null")
else:
match = re.search(r'/(FeatureServer|MapServer)/(\d+)$', url)
if match:
end_of_string = match.group(2) # Capture the number at the end
url_stripped = re.sub(r'/(FeatureServer|MapServer)/\d+$', r'/\1', url)
else:
url_stripped = url
new_feature = fmeobjects.FMEFeature(feature)
new_feature.setAttribute('layer_url', url_stripped)
new_feature.setAttribute('layer_fullrl', url)
new_feature.setAttribute('layer_path', path)
if end_of_string is not None:
new_feature.setAttribute('layer_url_end', end_of_string)
if item_id is not None:
new_feature.setAttribute('layer_itemId', item_id)
self.pyoutput(new_feature)