|
POST
|
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
... View more
Thursday
|
0
|
0
|
23
|
|
POST
|
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.")
... View more
Thursday
|
0
|
1
|
23
|
|
POST
|
Please provide some snips on how you are trying to use this expression.
... View more
Thursday
|
0
|
0
|
24
|
|
IDEA
|
Not only is the Measure Tool missing in some apps, but even when it is available, the behavior isn’t predictable. Example: You start measuring in feet… and suddenly the system switches to miles once you pass a threshold. That might make sense in theory—but in real workflows (telecom, construction, design), it creates unnecessary friction. Sometimes I need everything in feet. Not switching units mid-task. Not doing mental conversions. Just consistent output. We should be able to: Use the Measure Tool in every ArcGIS web app Define measurement units per map/project Keep units fixed (no automatic switching unless we choose it) This isn’t a feature request—it’s a usability standard. Consistent measurement = faster work, fewer mistakes, better user experience. If you’ve dealt with this, you know exactly how disruptive it is. Please make this consistent across the platform.
... View more
2 weeks ago
|
6
|
0
|
150
|
|
IDEA
|
I was referring to Web Editor and Webmap. Instant app is a nice workaround. Appreciate your feedback Emily.
... View more
2 weeks ago
|
0
|
0
|
88
|
|
IDEA
|
ArcGIS Dashboards spatial filters operate at the feature level, not the geometry level. When a line intersects a polygon, the dashboard includes the entire feature, and any displayed metrics (such as length) reflect the full geometry—even if only a portion lies within the selected area. This results in misleading analytics, such as overstated fiber length within service areas. Adding an optional ‘clip/intersect geometry’ mode would ensure that calculations are based only on the portion of features within the boundary, enabling accurate, analysis-ready dashboards without requiring data preprocessing.
... View more
4 weeks ago
|
2
|
0
|
218
|
|
POST
|
In ArcGIS Pro, you can visually “spatially filter” layers without editing data using clipping: Map Frame Clipping (Layout): Select the map frame → Map Frame tab → Clip → Clip to Shape → choose your polygon layer. This clips everything in the map frame so layers only display inside that boundary. Clip Layers in Map (Map View): In the Map Properties → Clip Layers, you can clip specific layers (or all layers) to a polygon, acting like a spatial filter without creating new data. Map Series Clipping: When using a map series, enable Clip to Index Feature so each page automatically clips the map to the current feature (e.g., per permit/cabinet area). All of these are display-only and don’t modify your data.
... View more
4 weeks ago
|
0
|
0
|
90
|
|
IDEA
|
A potential workaround is to create a view layer and split your categories across two instances of the same layer until Esri provides a way to extend the current limit. This approach allows you to effectively manage and display up to ~1800 categories without needing to publish an entirely separate layer. This can help maintain continuity in your workflows while avoiding the overhead of duplicating data or restructuring your content organization. Hopefully, this serves as a practical interim solution until ArcGIS Online increases the category limit.
... View more
4 weeks ago
|
0
|
0
|
122
|
|
IDEA
|
Thank you for sharing the resource — I’ll take a look at the basemap workflow. I understand and agree with the concern regarding projection distortion and the potential for grid-based measurements to be interpreted as exact. That said, from a practical workflow perspective, having the ability to define grid cell dimensions in real-world units (e.g., feet or meters) would still be very valuable. In many engineering and design use cases — especially at localized scales — users are already aware of projection limitations and are working within tolerances where that level of distortion is acceptable. Providing this capability, even with appropriate disclaimers, would significantly improve efficiency and alignment with existing workflows in tools like ArcGIS Pro. So yes, I would still prefer having the option to define grid cell dimensions, with the understanding that measurements may not be exact across the entire extent depending on the projection.
... View more
a month ago
|
0
|
0
|
200
|
|
IDEA
|
Yes, I am familiar. Hopefully this is something that can be implemented where user can save default selection settings so that we dont have to keep unchecking the boxes over 30 layers. Grouping helps but having ability to save preferred selectable layers by map would be very helpful similar to how Arc Pro works when you save project, selectable layers are also saved.
... View more
a month ago
|
0
|
0
|
183
|
|
POST
|
System-driven automations appear to have a significant impact on how custom symbology behaves in both ArcGIS Online and ArcGIS Pro. Specifically, when using Save Web Layer to update symbology driven by Shape_Length or Shape__Length, I’ve encountered cases where the symbology becomes corrupted. In those situations, the only reliable fix was to republish the data.
... View more
04-07-2026
08:20 AM
|
0
|
0
|
367
|
|
POST
|
I’m starting to suspect that relying on `Shape_Length` (or `Shape__Length` after export) for symbology in ArcGIS Pro can introduce subtle—but real—issues. Here’s what I’m seeing: * In FGDB vs enterprise GDB, length values can differ slightly due to parametric curves vs densified geometry * When exporting (e.g., to shapefile or CAD), `Shape_Length` often becomes `Shape__Length` or gets truncated * After that, symbology driven by those fields sometimes: * fails to apply * behaves inconsistently * or throws field-related errors when overriding symbology My assumption is that this is a combination of: * system-managed field behavior * field renaming/recreation during export * and ArcGIS Pro’s sensitivity to field references in renderers In workflows where symbology is tied to length (fiber segmentation, QA layers, etc.), this can get messy fast. Curious how others are handling this: * Do you avoid using `Shape_Length` for symbology altogether? * Do you copy values into a user-defined field to stabilize things? * Have you run into symbology breaking after data moves between FGDB / SDE / shapefile? Feels like one of those edge cases that turns into a real production issue if not handled carefully.
... View more
04-07-2026
08:13 AM
|
0
|
0
|
383
|
|
POST
|
I’ve noticed discrepancies between Shape_Length and Shape__Length when exporting feature classes to different formats (e.g., FGDB → shapefile / CAD). In some workflows they match, in others they don’t. How are you all handling this in production pipelines?
... View more
04-07-2026
07:31 AM
|
1
|
4
|
411
|
| Title | Kudos | Posted |
|---|---|---|
| 6 | 2 weeks ago | |
| 2 | 4 weeks ago | |
| 1 | 04-07-2026 07:31 AM | |
| 4 | 04-07-2026 05:50 AM | |
| 4 | 04-02-2026 01:27 PM |
| Online Status |
Offline
|
| Date Last Visited |
Friday
|