Select to view content in your preferred language

labeling a layer with the values of stand-alone table (that is associated to the layer with 1-Many relationship),

4953
23
Jump to solution
11-04-2016 01:03 PM
rawansaleh1
Occasional Contributor III

Hi All,

I wondering how could we labeling a layer with the values of stand-alone table (that is associated to the layer with 1-Many relationship),

 

For example, I wanted to label the parcels with their owners knowing that the parcel layer is associated with owners table with 1-Many relationship.

 

What might be the best practice to perform this task?

 

Thanks in advance,

Rawan

0 Kudos
23 Replies
RichardFairhurst
MVP Honored Contributor

Unicode is not a standard string of characters and you have to use unicode if you have non-English characters in your field.  Unicode has requirements that normal strings do not and is not supported by many string functions.  I don't use unicode strings and don't have time to research Python unicode requirements this week.  I also don't know if the ArcMap label generator has any issues with unicode (although it shouldn't if you get no error creating a label that just uses a field with Arabic letters as a normal label field).

For now just let me know if it works when you change line 37 to:

expression = labelKey

Hopefully that works and will create an error only on line 41, which you should then be able to change to:

expression += u'\n' + fieldValues[0]

If an error continues at line 37 insert the following line before line 37 and report the value that prints in the Python window:

print(labelKey)

0 Kudos
rawansaleh1
Occasional Contributor III

Dear Richard Fairhurst,

Thank you very much, its working fine finally without any error.

But the code “label the key field” and my goal is to label the “parcel number field” from layer and the “owner name” in the related table. Attached

# Initialize a global dictionary for a related feature class/table
relateDict = {}
def FindLabel ( [ParcelBlockCommunity] ):
  # 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:\Users\main\Documents\ArcGIS\ValuatedParcels_Objection.gdb\Owners"
    # create a field list with the relate field first (ParcelBlockCommunity),
    # followed by sort field(s) (ParcelNumber), then label field(s) (OwnerName_Arabic)
    relateFieldsList = ["ParcelBlockCommunity", "OwnerName_Arabic"]
    # 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 = [ParcelBlockCommunity]
  # start building a label expression.
  # My label has a bold key value header in a larger font
  expression = 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])
    # add a record count to the label header in bold regular font
    expression += '\n<FNT name="Times New Roman" size="10"><BOL>Owner Count = {}</BOL></FNT>'.format(len(sortedList))
    # process the sorted list
    for fieldValues in sortedList:
       # append related data to the label expression
       # my label shows a list of related
       # cross streets and measures sorted in driving order
       expression += '\n{}'.format(fieldValues[0])
    # clean up the list variables after completing the for loop
    del sortedList, fieldValues
  else:
    expression += '\n<FNT name="Arial" size="10"><BOL>Owner Count = 0</BOL></FNT>'
  # return the label expression to display
  return expression

Should I play or change the fields to get that works?

Thanks in advance,

Rawan

0 Kudos
RichardFairhurst
MVP Honored Contributor

Your expression needs to add any field in the parcel data to the findLabel method and then you need to built the label with the fields you want displayed, so yes you must add fields to the code and change how the label is constructed to use the field you want.

The screenshots you are providing are not enough for me to help you.  You must show me the table view for the Parcels table showing me both the ParcelBlockCommunity field and ParcelNumber (also the separate fields for Block and Community would be nice to see as well) in the table at the same time.  For the Owners table you need to show me both the ParcelBlockCommunity field and OwnerName_Arabic fields at the same time for the corresponding rows that match at least one of the parcels shown in the Parcel table screenshot.  The parcel record that has matching owners in the tables should also be shown on the map.  I have to be able to see the exact values that connect the two tables and that would be displayed in the label for an actual parcel and set of owners.

Also, why is only one label shown on the map? Shouldn't the map be displaying labels for all of the parcels in the map view?  How did you filter the label to only show parcel 32/53...  It is important that you let me know, since I want to be sure it won't affect how the label expression behaves.

I have revised the code based on the assumption that both tables have a field named ParcelBlockCommunity with identical values that match each parcel record to its corresponding owner records and that you want the label to display the ParcelNumber field and OwnerName_Arabic fields.  To make the parcel unique don't you also need to display the Block value in the label?  If you do, what is the name of the Block field?  It would have to be added to line 3 and line 37.  Let me know if any errors occur.

# Initialize a global dictionary for a related feature class/table
relateDict = {}
def FindLabel ( [ParcelBlockCommunity], [ParcelNumber] ):
  # 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:\Users\main\Documents\ArcGIS\ValuatedParcels_Objection.gdb\Owners"
    # create a field list with the relate field first (ParcelBlockCommunity),
    # followed by sort field(s) (ParcelNumber), then label field(s) (OwnerName_Arabic)
    relateFieldsList = ["ParcelBlockCommunity", "OwnerName_Arabic"]
    # 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 = [ParcelBlockCommunity]
  # start building a label expression.
  # My label has a bold key value header in a larger font
  expression = u'<FNT name="Times New Roman" size="14"><BOL>' + [ParcelNumber] + '</BOL></FNT>'
  # 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])
    # add a record count to the label header in bold regular font
    expression += u'\n<FNT name="Times New Roman" size="10"><BOL>Owner Count = {}</BOL></FNT>'.format(len(sortedList))
    # process the sorted list
    for fieldValues in sortedList:
       # append related data to the label expression
       # my label shows a list of related
       # cross streets and measures sorted in driving order
       expression += u'\n' + fieldValues[0]
    # clean up the list variables after completing the for loop
    del sortedList, fieldValues
  else:
    expression += '\n<FNT name="Arial" size="10"><BOL>Owner Count = 0</BOL></FNT>'
  # return the label expression to display
  return expression‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
rawansaleh1
Occasional Contributor III

Dear Richard Fairhurst,

Thank you very much for the elaboration and the massive effort, really appreciated.

I tired the above code,, it doesn’t produce any error but it still need more enhancement to achieve my goal.

Kindly find the following photos that show what you mentioned above.

I couldn’t figure out which type of changes I have to do to have the labeling as I want.

Any recommendation for this?

 

Thanks in advance

Rawan

0 Kudos
RichardFairhurst
MVP Honored Contributor

I have commented out line 31 and line 43.  Line 31 was indented wrong and causing it to interfere with the label generation beyond the first label, so I indebted it one more level (11/24/16 at 9:11 AM Pacific time).  Line 43 creates a count that you apparently do not want, except where no owners exist I assume.  Try this code:

# Initialize a global dictionary for a related feature class/table
relateDict = {}
def FindLabel ( [ParcelBlockCommunity], [ParcelNumber] ):
  # 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:\Users\main\Documents\ArcGIS\ValuatedParcels_Objection.gdb\Owners"
    # create a field list with the relate field first (ParcelBlockCommunity),
    # followed by sort field(s) (ParcelNumber), then label field(s) (OwnerName_Arabic)
    relateFieldsList = ["ParcelBlockCommunity", "OwnerName_Arabic"]
    # 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 = [ParcelBlockCommunity]
  # start building a label expression.
  # My label has a bold key value header in a larger font
  expression = u'<FNT name="Times New Roman" size="14"><BOL>' + [ParcelNumber] + '</BOL></FNT>'
  # 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])
    # add a record count to the label header in bold regular font
    # expression += u'\n<FNT name="Times New Roman" size="10"><BOL>Owner Count = {}</BOL></FNT>'.format(len(sortedList))
    # process the sorted list
    for fieldValues in sortedList:
       # append related data to the label expression
       # my label shows a list of related
       # cross streets and measures sorted in driving order
       expression += u'\n' + fieldValues[0]
    # clean up the list variables after completing the for loop
    del sortedList, fieldValues
  else:
    expression += '\n<FNT name="Arial" size="10"><BOL>Owner Count = 0</BOL></FNT>'
  # return the label expression to display
  return expression‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

This code separates each owner into its own line separated by a hard return. If you want more than one owner listed per line you will have to define the maximum number of characters you will allow per line and modify the loop at lines 45 to 49 to handle that format, but I am not prepared to suggest code that will accomplish that.  You should request help with that in a separate post or on a Python forum.

rawansaleh1
Occasional Contributor III

Dear Richard Fairhurst,

 

Thank you very much, actually I tried to use the previous code but I got the attached error.

What might the cause of this error?

 And I will post the question in a separate one to ask about how could I label more than one owner listed per line.

 

Best,

Rawan

0 Kudos
RichardFairhurst
MVP Honored Contributor

The code in my last post has been edited and is now fixed.  The code will produce all labels now.  Just use the code as it is now written.

I had previously tried to fix the error, but the post did not save my edits.  The error occurred because I had accidentally typed @ instead of # to comment out line 31.

rawansaleh1
Occasional Contributor III

Dear Richard Fairhurst,

 

Much appreciated its working fine with me.

 

Best,

Rawan

0 Kudos
RichardFairhurst
MVP Honored Contributor

I realized that line 31 is necessary to avoid a memory leak when matching large sets of records and that it was invented wrong and so I restored that line and indented it one more level in my last post with code.

Please use the revised code.

0 Kudos
rawansaleh1
Occasional Contributor III

Dear Richard Fairhurst,

 

Hi Again, Actually I would like to ask something about the code.

Is there an way to avoid having the “count =0 “ in the cases that we haven’t a related data? Attached

Also in some cases, we need to label a field which is used as a based field in a relationship and that is not applicable in the code.

And one more thing the code in some cases doesn’t labeling the featureclass/related table if we have a related table with Null values.

 

How could we enhance the code to deal with such cases?

 

Best,

Rawan

0 Kudos