Displaying multiple label features in one feature class

1281
7
12-13-2018 06:39 AM
SuriNo
by
New Contributor II

hello guys
I have a problem. I have a shp file with 8300 point information.
1. How does one value be displayed across multiple points?
2. Is there a different type of label on the label?
I'm trying to show pictures. Give me some time to help, please

7 Replies
RichardFairhurst
MVP Honored Contributor

As far as creating labels that cover a one to many relationship, see my blog on Creating Labels with Related Table Data.  In your case the related table is the points themselves.  You would use the apoint field as the dictionary key and the name field to build a list in the dictionary values.  You would want to check the option to remove duplicate labels to avoid having all of the points with the same apoint value create copies of the label

This label technique will be built to list all of the name values associated with a given apoint value, not just the set of names from the points that are currently visible on the map.  To limit the label names to just the visible points you would need the points to have coordinate fields populated and set a definition query that limited the points to the set that falls within the display boundary coordinates.

I don't think you can use graphic pictures in a label, but you can use fonts that contain those characters using the Window Character Map utility.  For example, I created this label:

Label with font based symbols

Using this label expression:

"<FNT name='Esri ERS Operations S1' size='12'><CLR red='255'>1 </CLR></FNT>" & [STREET_NAME] & vbcrlf & "<FNT name='Esri Cartography' size='12'><CLR blue='255'>Ê </CLR></FNT>" & [STREET_NAME] & vbcrlf & "<FNT name='Esri ArcPad' size='12'><CLR green='255'># </CLR></FNT>" & [STREET_NAME] 

I used filled symbols because I didn't find good versions of the unfilled symbols.  Usually the symbol outline was too thin on the few unfilled symbols I found.  Also I think it would be really challenging to find one or more fonts that gave all of the symbols the right thickness.

SuriNo
by
New Contributor II

Dear Richard

I've seen your blog, it's so amazing. I am glad for your help.
i used your label expression it is work. but it is not my searching result. Please, tell me
1. how i use "IF",
example:
Function FindLabel ( [type] )
if findlabel=1 then "<FNT name='Esri ERS Operations S1' size='12'>1</FNT>"
else
findlabel=2 then <FNT name='Esri Cartography' size='12'><CLR blue='255'>Ê </CLR></FNT>" ...
2. how puting other label [name] after those character? it is not similar [street_name]

0 Kudos
RichardFairhurst
MVP Honored Contributor

Try the code below in an advanced label expression by doing the following.  1. Change the parser to Python at the bottom of the label dialog.  2. Check the Advanced option checkbox.  3.  Remove any code you see in the expression dialog box and paste the code below in this post into the expression text box and modify the path in line 9 of the code below (relateFC = r"C:\your_path\deej") from "C:\your_path\deej" to your actual deej feature class path.

You have to change the path in line 9 to be the actual path to your deej point feature class, otherwise the code won't work.. If you have any problems please post the error message you get.

# Initialize a global dictionary for a related feature class/table
relateDict = {}
def FindLabel ( [apoint] ):
  # declare the dictionary global so it can be built once and used for all labels
  global relateDict
  # only populate the dictionary if it has no keys
  if len(relateDict) == 0:
    # Provide the path to the relate feature class/table
    relateFC = r"C:\your_path\deej"
    # create a field list with the relate field first (apoint), 
    # followed by sort field(s) (type), then label field(s) (name)
    relateFieldsList = ["apoint", "type", "name"]
    # process a da search cursor to transfer the data to the dictionary
    with arcpy.da.SearchCursor(relateFC, relateFieldsList) as relateRows:
      for relateRow in relateRows:
        # store the key value in a variable so the relate value 
        # is only read from the row once, improving speed
        relateKey = relateRow[0]
        # if the relate key of the current row isn't found 
        # create the key and make it's value a list of a list of field values
        if not relateKey in relateDict:
          # [searchRow[1:]] is a list containing
          # a list of the field values after the key.
          relateDict[relateKey] = [relateRow[1:]]
        else:
          # if the relate key is already in the dictionary 
          # append the next list of field values to the 
          # existing list associated with the key
          relateDict[relateKey].append(relateRow[1:])
    # delete the cursor, and row to make sure all locks release
    del relateRows, relateRow
  # store the current label feature's relate key field value
  # so that it is only read once, improving speed
  labelKey = [apoint]
  # start building a label expression.
  # My label has a bold key value header in a larger font
  expression = '<FNT name="Arial" size="12"><BOL>{}</BOL></FNT>'.format(labelKey)
  # determine if the label key is in the dictionary
  if labelKey in relateDict:
    # sort the list of the list of fields
    sortedList = sorted(relateDict[labelKey])
    # process the sorted list
    for fieldValues in sortedList:
       symbol = ""
       if fieldValues[0] == 1:
         symbol = "<FNT name='Esri Cartography' size='12'><CLR blue='255'>Ê </CLR></FNT>"
       elif fieldValues[0] == 2:
         symbol = "<FNT name='Esri ERS Operations S1' size='12'><CLR red='255'>1 </CLR></FNT>"
       elif fieldValues[0] == 3:
         symbol = "<FNT name='Esri ArcPad' size='12'><CLR green='255'># </CLR></FNT>" 
       # append related data to the label expression
       expression += '\n{0} - {1:.0f}'.format(symbol, fieldValues[1])
    # clean up the list variables after completing the for loop
    del sortedList, fieldValues
  else:
    expression += '\n<FNT name="Arial" size="10"><BOL>apoint Count = 0</BOL></FNT>'
  # return the label expression to display
  return expression‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
SuriNo
by
New Contributor II

Wow,
You can understand me very well, so I'm happy.
My basic knowledge of C ++ is elementary. So i cant fix a error.

0 Kudos
RichardFairhurst
MVP Honored Contributor

This is not C++, it is Python.  Anyway, did you change Line 9 to the path to your deej file?  if the path is wrong or the feature class name is wrong, that error might occur.  Please paste line 9 from the code you used and paste a picture of ArcCatalog showing the file path connection to your deej feature class.

The other likely reason is that one of your field names is not actually apoint, type or name, which are listed in line 12.  So are those the correct field names?

SuriNo
by
New Contributor II

Dear Richard


I found my mistake. your program is running. I'm sorry for creating a problem. I sent you the picture.
I have another question.
labels are duplicated when multiple points overlap.
Is the apoint value the same as one label?

Thank you very much for your patience

0 Kudos
RichardFairhurst
MVP Honored Contributor

I guess for points there is not an option to remove duplicate labels, and duplicates are produced by the code I gave you.  I have modified the code to output a blank label if the apoint value has already created a label, so no duplicate labels will be created.    You need to still modify line 11 for the path to your deej feature class (the code changed so it is line 10 and not line 9 that needs to change).

Duplicate labels will be eliminated whether or not the points actually overlap.  If you have some points that have the same apoint value but don't overlap and you want a label for each point when they don't overlap, then the code would have to examine the point geometry to only eliminate labels when the points actually overlap.  I did not make the code do that.  In order to even attempt to add subroutines for that I would need to know more about the scale of your map to determine how far apart the points have to be from each other before creating another label.  That is complexity that I won't tackle, since I don't want to devote the time it would take to make code that will work for any random scale you may want to print.  Someone else would have to contribute to this thread with ideas on how to tackle that if you want the labels to behave that way.

# Initialize a global dictionary for a related feature class/table
relateDict = {}
dupDict = {}
def FindLabel ( [apoint] ):
  # declare the dictionary global so it can be built once and used for all labels
  global relateDict
  global dupDict
  # only populate the dictionary if it has no keys
  if len(relateDict) == 0:
    # Provide the path to the relate feature class/table
    relateFC = r"C:\your_path\deej"
    # create a field list with the relate field first (apoint), 
    # followed by sort field(s) (type), then label field(s) (name)
    relateFieldsList = ["apoint", "type", "name"]
    # process a da search cursor to transfer the data to the dictionary
    with arcpy.da.SearchCursor(relateFC, relateFieldsList) as relateRows:
      for relateRow in relateRows:
        # store the key value in a variable so the relate value 
        # is only read from the row once, improving speed
        relateKey = relateRow[0]
        # if the relate key of the current row isn't found 
        # create the key and make it's value a list of a list of field values
        if not relateKey in relateDict:
          # [searchRow[1:]] is a list containing
          # a list of the field values after the key.
          relateDict[relateKey] = [relateRow[1:]]
        else:
          # if the relate key is already in the dictionary 
          # append the next list of field values to the 
          # existing list associated with the key
          relateDict[relateKey].append(relateRow[1:])
    # delete the cursor, and row to make sure all locks release
    del relateRows, relateRow
  # store the current label feature's relate key field value
  # so that it is only read once, improving speed
  labelKey = [apoint]
  # start building a label expression.
  # My label has a bold key value header in a larger font
  expression = '<FNT name="Arial" size="12"><BOL>{}</BOL></FNT>'.format(labelKey)
  # determine if the label key is in the dictionary
  if labelKey in relateDict:
    if not labelKey in dupDict:
      dupDict[labelKey] = labelKey
      # sort the list of the list of fields
      sortedList = sorted(relateDict[labelKey])
      # process the sorted list
      for fieldValues in sortedList:
        symbol = ""
        if fieldValues[0] == 1:
          symbol = "<FNT name='Esri Cartography' size='12'><CLR blue='255'>Ê </CLR></FNT>"
        elif fieldValues[0] == 2:
          symbol = "<FNT name='Esri ERS Operations S1' size='12'><CLR red='255'>1 </CLR></FNT>"
        elif fieldValues[0] == 3:
          symbol = "<FNT name='Esri ArcPad' size='12'><CLR green='255'># </CLR></FNT>" 
        # append related data to the label expression
        expression += '\n{0} - {1:.0f}'.format(symbol, fieldValues[1])
      # clean up the list variables after completing the for loop
      del sortedList, fieldValues
    else:
      expression = ""
  else:
    expression += '\n<FNT name="Arial" size="10"><BOL>apoint Count = 0</BOL></FNT>'
  # return the label expression to display
  return expression‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍