I've been on the struggle bus with getting this to work.
The general idea is:
1. Geocode a csv into a spatially enabled dataframe
2. Publish to Portal
3. Add to web map
4. Loop through layers of the web map
4a. Ensure they have the right display settings
4b. Remove old layers that had previously been added.
I've gotten all of the elements here to work independently but nothing I try can get them all to work together. When I fix something with the renderer then the changes to the pop-ups don't work or it's removing the wrong layer.
Right now, this is getting the correct layers in place but the changes I make to the operationalLayer are not reflected in the actual map and everything says its working so I dont know what I'm doing wrong.
# Enter newly imported CSV here:
csv_id = "a90b2637714c4bea82f1f938a705c0aa"
# In[2]:
# Imports of external libraries and functions to be used in the code
from arcgis.gis import GIS
from arcgis.geocoding import get_geocoders, suggest, geocode
from arcgis.features import FeatureLayer
from arcgis.mapping import WebMap, renderer
from datetime import datetime
import pandas as pd
import json
def download_file_item(gis: GIS, item_id: str, target_dir: str = '/arcgis/home') -> None:
file_item = gis.content.get(item_id)
file_download = file_item.download(save_path=target_dir)
if file_download:
print(f"Downloaded item [{item_id}] to workspace directory: {file_download}")
def router(location_in):
x.append(location_in[0])
y.append(location_in[1])
return x, y
def rank_suggestions(address_in, geocoder):
suggestions = suggest(address_in, geocoder=geocoder)
ranked_suggestions = {}
for suggestion_count, suggestion in enumerate(suggestions['suggestions']):
text = suggestion['text']
magic_key = suggestion['magicKey']
suggested_locations = geocode(text, magic_key, geocoder=geocoder, out_sr={'wkid': 4326})
ranked_suggestions[suggested_locations[0]['score']] = (
suggested_locations[0]['location']['x'], suggested_locations[0]['location']['y'])
sorted_suggestions = dict(sorted(ranked_suggestions.items()))
highest_score = list(ranked_suggestions)[0]
return sorted_suggestions[highest_score]
# These should stay the same unless you'd like to change the Web Map Item that is being used to display the information
wm_id = "0b442333de0544569191341e5e64dbad"
county_id = "3c2427bee22844bc953dfa9fb6304f25"
# Title to be used in naming the Feature Layer
title = "ScreenWise" + datetime.now().strftime('%B%Y')
# Properties of the Web Map so it isn't lost when we replace other parts of it.
webmap_properties = {'title': 'ScreenWise Provider Map',
'snippet': 'A map showing healthcare facilities under the ScreenWise Program',
'tags': 'Oregon Health Authority, OHA, ScreenWise, Providers'}
# Properties to tell the Web Map how to display the point symbols
renderer_properties = {'type': 'simple',
'label': title,
'description': '',
'rotationExpression': '',
'rotationType': 'arithmetic',
'visualVariables': [],
'symbol': {'type': 'esriSMS',
'style': 'esriSMSCircle',
# Color for symbol
'color': [255, 130, 0, 255],
'size': 8,
'angle': 0,
'xoffset': 0,
'yoffset': 0,
'outline': {'style': 'esriSLSSolid',
'type': 'esriSLS',
# Color for Outline
'color': [0, 0, 0, 255],
'width': 1}}}
# In[3]:
# This is the base of all modifications through the ArcGIS Online API
gis = GIS('https://geo.maps.arcgis.com/', username='chris.cowin_dhs', password=get_pass())
# This function takes the id of the newly uploaded csv file and uploads it to the python environment
# download_file_item(gis, csv_id)
# In[4]:
# Geocoders of the Portal
navigator = get_geocoders(gis)[0]
world = get_geocoders(gis)[1]
# Get csv and county item content so we can use the name
csv_item = gis.content.get(csv_id)
county_item = gis.content.get(county_id)
# Pull imported csv into Pandas Dataframe
# df = pd.read_csv('/arcgis/home/' + csv_item.name)
df = pd.read_csv(r'ScreenWiseProviders2024July.csv')
# Get Web Map that the content will go into
wm_item = gis.content.get(wm_id)
wm_data = wm_item.get_data()
wm = WebMap(wm_item)
# Combine csv rows into a single string to be fed into the Geocoders
addresses = [address.replace('\n', '') + ' ' + city + ', ' + state + ' ' + str(postal) for address, city, state, postal
in zip(df['Street_Address'], df['City'], df['State'], df['Zip'])]
# Empty lists for x and y coordinates to be added
x = []
y = []
# In[5]:
search = gis.content.search(query='title:' + title, item_type='Feature Layer')
if len(search) == 0:
# This loops through the rows of the csv
for count, address in enumerate(addresses):
# Try to geocode straight from the csv
print(str(count) + ': First Geocode...')
locations = geocode(address, geocoder=navigator, out_sr={'wkid': 4326})
# If that geocode contains a spot check the score
if len(locations) != 0:
# If the score is above 80 use it
if locations[0]['score'] > 80:
print(str(count) + f': ...Successfully Geocoded')
x, y = router((locations[0]['location']['x'], locations[0]['location']['y']))
# Else get suggestions on a better address
else:
print(str(count) + ': ...Geocode Score too low, getting suggestions')
suggested_location = rank_suggestions(address, navigator)
x, y = router(suggested_location)
else:
if ' or ' in address.lower():
print(str(count) + ': ...Geocode Failed, getting suggestions in OR')
suggested_location = rank_suggestions(address, navigator)
else:
print(str(count) + ': ...Geocode Failed, getting suggestions in US')
suggested_location = rank_suggestions(address, world)
if len(suggested_location) != 0:
x, y = router(suggested_location)
else:
x, y = router((0, 0))
print(str(count) + ': ......No found locations from suggestion, PLACED 0,0 YOU WILL NEED TO PLACE!')
# This inserts the x and y coordinates into the dataframe used to get the addresses
df['x'] = [_ for _ in x]
df['y'] = [_ for _ in y]
# Then those columns are used to generate a shape object withing the DataFrame
sedf = pd.DataFrame.spatial.from_xy(df, 'x', 'y', sr=4326)
fl = sedf.spatial.to_featurelayer(title=title, gis=gis, service_name=title, sanitize_columns=True)
# Just wrote this so I wasnt geocoding the same df over and over if it exists
elif len(search) == 1:
fl = search[0]
else:
raise Exception('More than one ScreenWise Layer Found')
# In[49]:
wm.add_layer(fl, renderer_properties)
layers = wm.layers
wanted_layers = [fl.id, county_id]
for i, layer in enumerate(layers):
if layer.itemId in wanted_layers:
if layer.itemId == county_id:
for operational_layer in wm_data['operationalLayers']:
if operational_layer['itemId'] == county_id:
print(layer['itemId'] + ' removing popups')
layer['disablePopup'] = True
break
elif layer.itemId == fl.id:
for operational_layer in wm_data['operationalLayers']:
if operational_layer['itemId'] == fl.id:
print(layer.keys())
print(layer['title'] + ' :: ' + layer['itemId'])
operational_layer['title'] = title
operational_layer['disablePopup'] = False
operational_layer['popupInfo']['title'] = '{Clinic_Sit}'
operational_layer['popupInfo']['fieldInfos'][0]['visible'] = False
operational_layer['popupInfo']['fieldInfos'][1]['label'] = 'Agency Name'
operational_layer['popupInfo']['fieldInfos'][2]['visible'] = False
operational_layer['popupInfo']['fieldInfos'][3]['label'] = 'Street Address'
operational_layer['popupInfo']['fieldInfos'][9]['label'] = 'Website'
operational_layer['popupInfo']['fieldInfos'][10]['label'] = 'Hours of Operation'
operational_layer['popupInfo']['fieldInfos'][11]['label'] = 'Accepting New Patients'
operational_layer['popupInfo']['fieldInfos'][12]['label'] = 'Schedule an Appointment By'
operational_layer['popupInfo']['fieldInfos'][13]['label'] = 'Bilingual Medical Provider (Yes/No)'
operational_layer['popupInfo']['fieldInfos'][14]['label'] = 'Bilingual Medical Provider Language'
operational_layer['popupInfo']['fieldInfos'][15]['label'] = 'Onsite Interpretation Services (Yes/No)'
operational_layer['popupInfo']['fieldInfos'][16]['label'] = 'Onsite Interpretation Services Language'
operational_layer['popupInfo']['fieldInfos'][17]['label'] = 'Telehealth Interpretation Services (Yes/No)'
operational_layer['popupInfo']['fieldInfos'][18]['label'] = 'Telehealth Interpretation Services Language'
operational_layer['popupInfo']['fieldInfos'][19]['visible'] = False
operational_layer['popupInfo']['fieldInfos'][20]['visible'] = False
break
else:
wm.remove_layer(layers[i])
# Dump that dictionary into a json and update the web map
item_properties = {'text': json.dumps(wm_data)}
wm.update(item_properties=item_properties)
Hi @ChrisCowin_dhs,
When you update a WebMap object via one of its properties or calling a method, you need to use the WebMap class update() method.
wm.add_layer()
wm.remove_layer()
wm.update()
When you directly manipulate a (WebMap) Item objects JSON, like the operationalLayers, you need to update the Item object, not the WebMap object.
for operational_layer in wm_data['operationalLayers']:
if operational_layer['itemId'] == county_id:
print(layer['itemId'] + ' removing popups')
layer['disablePopup'] = True
item_properties = {'text': json.dumps(wm_data)}
wm_item.update(item_properties=item_properties)
I recommend doing the workflow in the following stages.
1) Get your WebMap layers the way you want them with adding and removing and use the WebMap object update() (for wm variable in your code) to update the WebMap
2) Re-access / re-instantiate the WebMap object so it now reflects the changes in 1 above.
3) Perform the operationLayer edits and apply the update to the (WebMap) Item object (for wm_item in your code)
Made some changes from your suggestions but now when I try to add the layer it doesn't add the layer!
Tried updating the WebMap object:
wm.add_layer(fl, renderer_properties)
wm.update()
Tried updating the web map item:
wm.add_layer(fl, renderer_properties)
wm_item.update()
If you set the add_layer() call to a variable and print to screen does it return True or False.
status = wm.add_layer(fl, renderer_properties)
print(status)
and then the same for the update()
status = wm.update()
print(status)
Your rendererer_properties dictionary is probably the problem if False is returned for the add_layer()
No, it returns True but doesn't actually show up