Displaying Labels Using Python API

1327
4
Jump to solution
05-17-2023 10:28 AM
Labels (2)
Mateya_Turmel
New Contributor II

Hello,


I am attempting to use the Python API in order to publish Feature Services to an ArcGIS Online portal. However, I am having a problem getting labels to appear. I have been trying to use the "showLabels" property in order to have the labels display. I tried including the "showLabels" property in "drawingInfo" (as per https://developers.arcgis.com/web-map-specification/objects/drawingInfo/) and also more just within the feature layer definition (not nested in drawingInfo). Neither have had any success.

When I go into the portal, the labels do not appear by default. If I click on "Create Labels" for the layer then it will load them properly with the correct labelingInfo. I was verifying the JSON using the inspect tool and noticed that the "showLabels" property found in arcgisonline.map.main.mapLayers[id] was being set as undefined. If I manually switch it to true and then reload the feature layer, then the labels appear.

My best guess right now is that I am setting showLabels in the incorrect spot and it isn't being recognized properly when loading the map. Is there somewhere else I need to set showLabels or another property that I need to modify?

 

Below is the rough structures I have tried. Every other property is being set correctly so I don't think there is an error trying to update the layer. 

Structue within "drawingInfo"

{

    "id": 0

    "drawingInfo": {

        "showLabels": True

        ...

    }

    ...

}

 

Structue not within "drawingInfo"

{

    "id": 0

    "showLabels": True

    "drawingInfo": { ... }

    ...

}

Any help would be greatly appreciated, thanks!

 

Note: This issue has been encountered before so I'm not sure if it is maybe a bug with the Python API (https://community.esri.com/t5/arcgis-api-for-python-questions/labelling-not-showing-on-webmap/td-p/1...

1 Solution

Accepted Solutions
Clubdebambos
Occasional Contributor III

Hi @Mateya_Turmel 

The following should help you out. Im assuming that you are attempting to add labels directly to the Feature Service and not a layer in a WebMap.

You turn on labels in the item data and apply the label style in the item properties as shown in the commented code below. 

 

 

from arcgis import GIS
import json

## access AGOL
agol = GIS("home")

## this is a template of the label definition
## see JSON below in another code snippet
json_file = r"path\to\label_template.json"

## access the feature service item 
item = agol.content.get("FS_ITEM_ID")
## update the lyr_id if there are more than one layers and you need
## to target a different one
lyr_id = 0

## access the item data json
item_data = item.get_data()

for data_lyr in item_data["layers"]:
    if data_lyr["id"] == lyr_id:
        ## set showLables property to True
        data_lyr["showLabels"] = True
        ## save the new definition
        item_properties = {"text":item_data}
        item.update(item_properties=item_properties)

## access the layer in the FS Item
lyr = item.layers[lyr_id]

## open the json template
with open(json_file) as json_data:
    ## grab the json label definition
    loaded_json = json.load(json_data)

    ## update the drawingInfo defintion labellingInfo property
    lyr.properties["drawingInfo"]["labelingInfo"] = loaded_json

## update the FS Definition
lyr.manager.update_definition({"drawingInfo": lyr.properties["drawingInfo"]})

 

 

The label_template.json file looks like the below.

 

 

[
  {
        "labelExpression": null,
        "labelExpressionInfo": {
          "expression": "$feature[\"name\"]",
          "value": "{name}"
        },
        "useCodedValues": true,
        "maxScale": 0,
        "minScale": 0,
        "where": null,
        "labelPlacement": "esriServerPolygonPlacementAlwaysHorizontal",
        "symbol": {
          "color": [
            51,
            51,
            51,
            255
          ],
          "type": "esriTS",
          "backgroundColor": null,
          "borderLineColor": null,
          "haloSize": 0.75,
          "haloColor": [
            255,
            255,
            255,
            255
          ],
          "horizontalAlignment": "center",
          "rightToLeft": false,
          "angle": 0,
          "xoffset": 0,
          "yoffset": 0,
          "text": "",
          "rotated": false,
          "kerning": true,
          "font": {
            "size": 9.75,
            "style": "normal",
            "decoration": "none",
            "weight": "bold",
            "family": "Arial"
          }
        }
      }
]

 

 

 

This can be obtained by applying labels manually in the Visualization tab and saving the layer and then using the below to print to screen. 

 

 

print(lyr.properties["drawingInfo"]["labelingInfo"])

 

 

 Once you get to grips with the label labellingInfo json formatting you can create and alter the template to suit any layer. In the example above I am labelling the 'name' field in my layer as shown in the "expression" and "value" properties.

~ learn.finaldraftmapping.com

View solution in original post

0 Kudos
4 Replies
Clubdebambos
Occasional Contributor III

Hi @Mateya_Turmel 

The following should help you out. Im assuming that you are attempting to add labels directly to the Feature Service and not a layer in a WebMap.

You turn on labels in the item data and apply the label style in the item properties as shown in the commented code below. 

 

 

from arcgis import GIS
import json

## access AGOL
agol = GIS("home")

## this is a template of the label definition
## see JSON below in another code snippet
json_file = r"path\to\label_template.json"

## access the feature service item 
item = agol.content.get("FS_ITEM_ID")
## update the lyr_id if there are more than one layers and you need
## to target a different one
lyr_id = 0

## access the item data json
item_data = item.get_data()

for data_lyr in item_data["layers"]:
    if data_lyr["id"] == lyr_id:
        ## set showLables property to True
        data_lyr["showLabels"] = True
        ## save the new definition
        item_properties = {"text":item_data}
        item.update(item_properties=item_properties)

## access the layer in the FS Item
lyr = item.layers[lyr_id]

## open the json template
with open(json_file) as json_data:
    ## grab the json label definition
    loaded_json = json.load(json_data)

    ## update the drawingInfo defintion labellingInfo property
    lyr.properties["drawingInfo"]["labelingInfo"] = loaded_json

## update the FS Definition
lyr.manager.update_definition({"drawingInfo": lyr.properties["drawingInfo"]})

 

 

The label_template.json file looks like the below.

 

 

[
  {
        "labelExpression": null,
        "labelExpressionInfo": {
          "expression": "$feature[\"name\"]",
          "value": "{name}"
        },
        "useCodedValues": true,
        "maxScale": 0,
        "minScale": 0,
        "where": null,
        "labelPlacement": "esriServerPolygonPlacementAlwaysHorizontal",
        "symbol": {
          "color": [
            51,
            51,
            51,
            255
          ],
          "type": "esriTS",
          "backgroundColor": null,
          "borderLineColor": null,
          "haloSize": 0.75,
          "haloColor": [
            255,
            255,
            255,
            255
          ],
          "horizontalAlignment": "center",
          "rightToLeft": false,
          "angle": 0,
          "xoffset": 0,
          "yoffset": 0,
          "text": "",
          "rotated": false,
          "kerning": true,
          "font": {
            "size": 9.75,
            "style": "normal",
            "decoration": "none",
            "weight": "bold",
            "family": "Arial"
          }
        }
      }
]

 

 

 

This can be obtained by applying labels manually in the Visualization tab and saving the layer and then using the below to print to screen. 

 

 

print(lyr.properties["drawingInfo"]["labelingInfo"])

 

 

 Once you get to grips with the label labellingInfo json formatting you can create and alter the template to suit any layer. In the example above I am labelling the 'name' field in my layer as shown in the "expression" and "value" properties.

~ learn.finaldraftmapping.com
0 Kudos
Mateya_Turmel
New Contributor II

Thank you very much for your reply @Clubdebambos  and for going as in-depth as you did!

To clarify, I have been attempting to set showLabels within the feature layer and not within the service. However, I have been using a slightly different technique than what you have done.

from arcgis import features, GIS

# Get the feature service item
item = agol.content.get("Feature Service ID")

# Convert to FeatureLayerCollection object
flc = features.FeatureLayerCollection.fromitem(item)

# Add/append layer definitions
flc.manager.add_to_definition({"layers": [layer_definitions]})

 

Each definition in layer_definitions is roughly as follows

{
    "type": "Feature Layer",
    "extent": {
        "xmax": 180.0,
        "ymin": -90.0,
        "xmin": -180.0,
        "ymax": 90.0,
        "spatialReference": {
            "wkid": 4326,
            "latestWkid": 4326
        }
    },
    "hasAttachments": false,
    "name": <layer_name>,
    "fields": [
        {
            "alias": <field_alias>,
            "name": <field_name>,
            "type": "esriFieldTypeString"
        }
    ],
    "drawingInfo": {
        "renderer": {
            "type": "simple",
            "symbol": {
                "type": "esriSMS",
                "color": [
                    66,
                    135,
                    245,
                    255
                ],
                "style": "esriSMSCircle",
                "size": 10,
            }
        },
        "scaleSymbols": false,
        "labelingInfo": [
            {
                "labelExpressionInfo": {
                    "expression": "$feature[\"<name>\"]"
                },
                "labelPlacement": "esriServerPointLabelPlacementCenterRight",
                "minScale": 0,
                "maxScale": 0,
                "symbol": {
                    "color": [
                        51,
                        51,
                        51,
                        255
                    ],
                    "font": {
                        "decoration": "none",
                        "family": "Arial",
                        "size": 8,
                        "style": "normal",
                        "weight": "normal"
                    },
                    "haloColor": null,
                    "haloSize": null,
                    "horizontalAlignmen": "center",
                    "kerning": true,
                    "rightToLeft": false,
                    "type": "esriTS",
                    "verticalAlignment": "baseline",
                    "xoffset": 0,
                    "yoffset": 0
                },
                "useCodedValues": false
            }
        ],
        "transparency": 0
    },
    "showLabels": true,
    "geometryType": "esriGeometryPoint"
}

 

Every other parameter works as expected, except for showLabels. I end up with a bunch of points that have no labels. If I go into the layer settings on the web map and click "Create Labels", then they show up with the information specified in labelingInfo.

I was going to try to use your method, but I am running into a slight issue. When I use get_data() on the item returned from the feature service, it is returning an empty dictionary. I don't know if that could indicate a larger issue with my creation of the feature service. Additionally, I can see in the returned item object that 'layers' is None despite the fact that I can see my created layers when viewing the map in the portal. Do either of these issues indicate a problem with my setup of the service and it's layers? If yes, could this be causing the labels to not render properly? Given that everything else is working as intended, I believe I set it up correctly, but perhaps I am mistaken.

Thanks again for your time!

0 Kudos
Mateya_Turmel
New Contributor II

The solution by @Clubdebambos is correct. However, I would like to add a little more detail for people facing a similar problem. As far as I can tell, the issue is that you must set the 'showLabels' property on the Item object not a FeatureLayerCollection object. I ran into an issue where the 'layers' property in my feature service Item was None. To fix this, I had to manually add the layer definition using Item.update({"text": {"layers": [layer]}}). I'm not sure if this is the best solution, but it worked for me.

layer = {
            "id": 0,
            "showLabels": True,
            "popupInfo": {
                "title": "A_TITLE",
                "popupElements": [
                    {
                        "fieldInfos": [
                            {
                                "fieldName": "FIELD_NAME1",
                                "isEditable": False,
                                "visible": True,
                                "label": "FIELD_LABEL1"
                            },
                            {
                                "fieldName": "FIELD_NAME2",
                                "isEditable": False,
                                "visible": True,
                                "label": "FIELD_LABEL2"
                            },
                            {
                                "fieldName": "FIELD_NAME3",
                                "isEditable": False,
                                "visible": True,
                                "label": "FIELD_LABEL3"
                            }
                        ],
                        "type": "fields"
                    }
                ],
                "fieldInfos": [
                    {
                        "fieldName": "FIELD_NAME1",
                        "isEditable": False,
                        "visible": True,
                        "label": "FIELD_LABEL1"
                    },
                    {
                        "fieldName": "FIELD_NAME2",
                        "isEditable": False,
                        "visible": True,
                        "label": "FIELD_LABEL2"
                    },
                    {
                        "fieldName": "FIELD_NAME3",
                        "isEditable": False,
                        "visible": True,
                        "label": "FIELD_LABEL3"
                    }
                ]
            }
        }

# Where gis is an instance of arcgis.GIS
service = gis.content.get(<SERVICE_ID>)

# If there are multiple layers, create multiple instances of the dictionary above
service.update(item_properties= {"text": {"layers": [layer]}})

 

As a side note, make sure that the IDs match or your layer will not appear. Additionally, you will need to define all layers using a similar notation as above (even if they do not have labels). From what I can tell, it seems like if you define the layer properties in the FeatureLayerCollection object then it will auto-generate the above JSON. However, if you manually set the layers property (to show the labels, for instance) then you will need to manually create the JSON for each layer.

I'm not sure if that is the best explanation, however, please feel free to reply if you have more questions!

0 Kudos
lucyhills
New Contributor

It looks like you're working with the ArcGIS API for Python to publish Feature Services to an ArcGIS Online portal and are having trouble getting labels to appear by default. The issue might be related to how the "showLabels" property is set in the JSON structure.

In the ArcGIS REST API, the "showLabels" property is typically set within the "layerDefinition" of a feature layer. Here's an example structure that you can try:

 

from arcgis.gis import GIS
from arcgis.features import FeatureLayer

# Connect to your ArcGIS Online portal
gis = GIS("home")

# Create a feature layer
feature_layer_properties = {
"name": "Your Feature Service Name",
"capabilities": "Create,Delete,Query,Update,Editing",
"displayField": "Name", # Replace with your display field
"geometryType": "esriGeometryPoint", # Replace with your geometry type
"fields": [ # Add your fields here
{"name": "Name", "type": "esriFieldTypeString", "alias": "Name", "length": 100}
],
"objectIdField": "OBJECTID", # Replace with your object ID field
"sourceSpatialReference": {"wkid": 4326}, # Replace with your spatial reference
"geometryService": "http://sampleserver6.arcgisonline.com/arcgis/rest/services/Utilities/Geometry/GeometryServer",
"maxRecordCount": 2000,
"maxScale": 0,
"minScale": 0,
"allowGeometryUpdates": True,
"xssPreventionInfo": {"xssPreventionEnabled": True, "xssPreventionRule": "InputOutput"},
"tables": [],
"isDataVersioned": False,
"metadata": "",
"showLabels": True, # Set showLabels to True
"supportsAppend": True,
"supportsCalculate": True,
"supportsAttachmentsByUploadId": True,
}

# Publish the feature layer
feature_layer = gis.content.import_data(
data=your_data, # Replace with your data
title="Your Feature Layer Title",
tags="your, tags", # Replace with your tags
folder="Your Folder", # Replace with your folder
overwrite=True,
create_layer=True,
layer_properties=feature_layer_properties,
)

# Display the URL to the feature layer
print(feature_layer.url)

 

In this example, the "showLabels" property is set directly under the "layerDefinition" as part of the "feature_layer_properties" dictionary. Adjust the other properties based on your b2b data building and requirements.

0 Kudos