Select to view content in your preferred language

Edits to operational layer are not shown on update to web map

323
4
08-19-2024 04:00 PM
Labels (1)
ChrisCowin_dhs
Occasional Contributor

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)
0 Kudos
4 Replies
Clubdebambos
Frequent Contributor

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)

~ learn.finaldraftmapping.com
0 Kudos
ChrisCowin_dhs
Occasional Contributor

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()

 

 

0 Kudos
Clubdebambos
Frequent Contributor

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()

~ learn.finaldraftmapping.com
0 Kudos
ChrisCowin_dhs
Occasional Contributor

No, it returns True but doesn't actually show up

0 Kudos