Select to view content in your preferred language

Displaying layer name in pop-up

2352
8
04-08-2013 02:40 PM
RichardKeightley
Emerging Contributor
Hi all,

I have a webmap with various layers, configured to display pop-ups.

The issue I have is that the layers are very close to each other spatially and sometimes overlap, so it is sometimes unclear which layer you have clicked and which layer ArcGIS online is displaying information for.

Is there a way to display the layer name in the pop-up window?

Thanks
Richard
Tags (2)
0 Kudos
8 Replies
MikeMinami
Esri Notable Contributor
Yes, when you configure the popup, you can specify a title. You can type in the layer name as the title.

Thanks,

Mike
0 Kudos
RichardKeightley
Emerging Contributor
Thanks Mike,

I must have been having a brain meltdown yesterday!  I was forgetting the pop up is specific to each layer...

Cheers
Richard
0 Kudos
Miralem_Zeljo
Frequent Contributor

The default was that layer name was displayed unless you change it to be driven by the attribute. How do we set up expression to show layer name without typing this in manually for every layer?

0 Kudos
Miralem_Zeljo
Frequent Contributor

We found a workaround so you dont have to type this in for every layer: return GetFeatureSetInfo($layer).webMapLayerTitle is the expression that returns the layer name in the popup.

laurentideicesheet
Regular Contributor

Thanks for this! The expression isn't working in my Arcade item in the popup. Did you envision it elsewhere?

0 Kudos
Miralem_Zeljo
Frequent Contributor

Please provide some snips on how you are trying to use this expression.

0 Kudos
Miralem_Zeljo
Frequent Contributor

If you want to do this for all the layers in the map you can use notebooks to automate it: 

from arcgis.gis import GIS
import json
import copy

gis = GIS("home")

WEBMAP_ID = "PASTE_WEBMAP_ITEM_ID_HERE"

expr_name = "expr_layer_name"
expr_title = "Layer Name"
expr_code = """
var info = GetFeatureSetInfo(GetFeatureSet($feature));
return info.webMapLayerTitle;
""".strip()

item = gis.content.get(WEBMAP_ID)
data = item.get_data()

for lyr in data.get("operationalLayers", []):
# Skip layers without popups if desired
popup = lyr.setdefault("popupInfo", {})

# Add expressionInfos if missing
expressions = popup.setdefault("expressionInfos", [])

# Avoid duplicate expression
existing = next((e for e in expressions if e.get("name") == expr_name), None)

if existing:
existing["title"] = expr_title
existing["expression"] = expr_code
else:
expressions.append({
"name": expr_name,
"title": expr_title,
"expression": expr_code
})

# Add to description without overwriting existing popup content
desc = popup.get("description", "")

layer_line = f"Layer: {{expression/{expr_name}}}<br>"

if f"{{expression/{expr_name}}}" not in desc:
popup["description"] = layer_line + desc

item.update(item_properties={
"text": json.dumps(data)
})

print("Updated popups for all operational layers.")

0 Kudos
Miralem_Zeljo
Frequent Contributor

Or something like this if you want to keep all other fields in popup:

# --- Web map popups: Title = Layer Name, Body = All Fields table (like your screenshot) ---
from arcgis.gis import GIS
from arcgis.features import FeatureLayer
import json, datetime

WEBMAP_ID = "yourmapid" # your map
DRY_RUN = False # True = preview, don't save
INCLUDE_SYSTEM_FIELDS = True # keep OBJECTID/SHAPE_*/editor tracking, etc.

# Arcade expression: prefer the web map's layer title; fallback to service layer name
ARC_EXPR = """
var info = GetFeatureSetInfo($layer);
var t = info.webMapLayerTitle;
var n = info.layerName;
return IIf(IsEmpty(t), IIf(IsEmpty(n), 'Layer', n), t);
"""

def _expr_infos():
return [{
"name": "expr0",
"title": "Layer Name",
"expression": ARC_EXPR,
"returnType": "string"
}]

def _get_fields_from(layer_dict, gis):
"""Return list of {'fieldName','label','visible'} in the service order."""
# 1) Try popupInfo.fieldInfos first (to preserve existing aliases/order if present)
pi = layer_dict.get("popupInfo")
if isinstance(pi, dict):
finfos = pi.get("fieldInfos")
if isinstance(finfos, list) and finfos:
out = []
for f in finfos:
if not isinstance(f, dict):
continue
name = f.get("fieldName") or f.get("name")
if not name:
continue
out.append({"fieldName": name, "label": f.get("label", name), "visible": True})
if out:
return out

# 2) layerDefinition.fields (common for map-image sublayers / feature collections)
ld = layer_dict.get("layerDefinition")
if isinstance(ld, dict) and isinstance(ld.get("fields"), list) and ld["fields"]:
out = []
for f in ld["fields"]:
nm = f.get("name")
if not nm:
continue
out.append({"fieldName": nm, "label": f.get("alias", nm), "visible": True})
if out:
return out

# 3) Service fields via URL
url = layer_dict.get("url")
if url:
try:
fl = FeatureLayer(url, gis=gis)
fields = getattr(fl.properties, "fields", None)
out = []
if isinstance(fields, list):
for f in fields:
nm = f.get("name")
if not nm:
continue
out.append({"fieldName": nm, "label": f.get("alias", nm), "visible": True})
if out:
return out
except Exception:
pass

# 4) FeatureCollection inner layerDefinition
fc = layer_dict.get("featureCollection")
if isinstance(fc, dict):
for fcl in (fc.get("layers") or []):
fld = fcl.get("layerDefinition")
if isinstance(fld, dict) and isinstance(fld.get("fields"), list) and fld["fields"]:
out = []
for f in fld["fields"]:
nm = f.get("name")
if not nm:
continue
out.append({"fieldName": nm, "label": f.get("alias", nm), "visible": True})
if out:
return out

return []

def _maybe_filter_system(fields):
if INCLUDE_SYSTEM_FIELDS:
return fields
sys_prefixes = ("SHAPE",)
exclude_exact = {"OBJECTID","FID","GLOBALID","GLOBAL_ID",
"CreationDate","Creator","EditDate","Editor",
"created_date","created_user","last_edited_date","last_edited_user"}
out = []
for f in fields:
nm = f["fieldName"]
up = nm.upper()
if up in exclude_exact or any(up.startswith(p) for p in sys_prefixes):
continue
out.append(f)
return out

def _ensure_popup(layer_dict, gis, path, changes):
"""Replace popup with: title expr + fields table; applies to any schema location."""
changed = False

# enable popups if disabled
if layer_dict.get("disablePopup") is True:
layer_dict["disablePopup"] = False
changed = True
changes.append(f"{path}: enabled popups")

# locations where popupInfo may live
spots = []

# direct
if isinstance(layer_dict.get("popupInfo"), dict):
spots.append(("popupInfo", layer_dict["popupInfo"]))
else:
layer_dict["popupInfo"] = {}
spots.append(("popupInfo(created)", layer_dict["popupInfo"]))
changed = True

# map-image sublayer
ld = layer_dict.get("layerDefinition")
if isinstance(ld, dict):
if isinstance(ld.get("popupInfo"), dict):
spots.append(("layerDefinition.popupInfo", ld["popupInfo"]))
else:
ld["popupInfo"] = {}
spots.append(("layerDefinition.popupInfo(created)", ld["popupInfo"]))
layer_dict["layerDefinition"] = ld
changed = True

# feature collection inner layers
fc = layer_dict.get("featureCollection")
if isinstance(fc, dict):
for idx, fcl in enumerate(fc.get("layers") or []):
if isinstance(fcl.get("popupInfo"), dict):
spots.append((f"featureCollection.layers[{idx}].popupInfo", fcl["popupInfo"]))
else:
fcl["popupInfo"] = {}
spots.append((f"featureCollection.layers[{idx}].popupInfo(created)", fcl["popupInfo"]))
changed = True

# unified fields list (service order + aliases)
fields = _get_fields_from(layer_dict, gis)
fields = _maybe_filter_system(fields)

for label, pi in spots:
# Title via Arcade
pi["title"] = "{expression/expr0}"
pi["expressionInfos"] = _expr_infos()

# Replace body with a single fields table exactly like the viewer's table
pi["popupElements"] = [{"type": "fields", "fieldInfos": fields}]

# For older viewers, keep fieldInfos mirrored too (order/labels)
pi["fieldInfos"] = fields

# Clean extras so only table shows (like your screenshot)
pi["description"] = None
pi["mediaInfos"] = []
pi["showAttachments"] = False

changed = True

if changed:
changes.append(f"{path}: enforced title+fields-table ({len(fields)} fields)")
return changed

def _walk(node, gis, path, changes):
changed = False
if isinstance(node, dict):
if _ensure_popup(node, gis, path, changes):
changed = True
# Recurse children
for i, child in enumerate(node.get("layers") or []):
if isinstance(child, dict):
if _walk(child, gis, f"{path}.layers[{i}]", changes):
changed = True
for i, child in enumerate(node.get("tables") or []):
if isinstance(child, dict):
if _walk(child, gis, f"{path}.tables[{i}]", changes):
changed = True
fc = node.get("featureCollection")
if isinstance(fc, dict):
for i, fcl in enumerate(fc.get("layers") or []):
if isinstance(fcl, dict):
if _walk(fcl, gis, f"{path}.featureCollection.layers[{i}]", changes):
changed = True
return changed

def run(webmap_id, dry_run=False):
gis = GIS("home")
item = gis.content.get(webmap_id)
if not item:
raise RuntimeError("Web map not found")
data = item.get_data()
if not data:
raise RuntimeError("No web map data")

print(f"Loaded: {item.title} ({item.id})")
changes = []
changed_any = False

for i, lyr in enumerate(data.get("operationalLayers", []) or []):
if _walk(lyr, gis, f"operationalLayers[{i}]", changes):
changed_any = True
for i, tbl in enumerate(data.get("tables", []) or []):
if _walk(tbl, gis, f"tables[{i}]", changes):
changed_any = True

if not changed_any:
print("No changes necessary.")
return {"updated": False, "changes": changes}

backup = f"webmap_backup_{item.id}_{datetime.datetime.utcnow().strftime('%Y%m%dT%H%M%SZ')}.json"
with open(backup, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2)
print(f"Backup saved: {backup}")

if dry_run:
print("\nDRY RUN — sample changes:")
for line in changes[:30]:
print(" •", line)
if len(changes) > 30:
print(f"... plus {len(changes)-30} more")
return {"updated": False, "changes": changes, "backup": backup}

ok = item.update(data=data)
if not ok:
raise RuntimeError("Item update returned False")

print("\n Updated. Open the **web map** (not an app) in a NEW tab and hard-refresh (Ctrl+F5).")
for line in changes[:30]:
print(" •", line)
if len(changes) > 30:
print(f"... plus {len(changes)-30} more")
return {"updated": True, "changes": changes, "backup": backup}

result = run(WEBMAP_ID, DRY_RUN)
result

0 Kudos