Dynamic Text Table Attribute & Arcade Expression for Street Index

1496
7
Jump to solution
08-10-2022 04:49 AM
LindaKuhn
New Contributor II

I have a street map with a Street Index listing street names in alphabetical order and showing which section(s) those street are listed in.  I am trying to use the Dynamic Text - Table Attribute to create the Street Index from an Excel spreadsheet.  

LindaKuhn_0-1660130232237.png

I am using an Arcade expression to count the number of characters in the street name and the section(s) to then determine how many leader dots to place in between the street name and the section.  I've decided on a total character length of 30 as this should hold the longest street name and the longest section(s) and still allow a few leader dots in between the two.

This is a small sample of my Excel spreadsheet:

LindaKuhn_3-1660130856126.png

Column A contains the street names and Column B contains the section(s) that particular street is found in on the map.

Looking at my Text Element, from the Insert Table Attribute I am using an Arcade expression.  I want the Arcade expression to add a blank row between the numeric street names and another blank row between the street names that start with A and another blank row between the street names that start with B, etc. to the end of the alphabet.  I am using the Arcade expression to count the total number of characters found in the street name and section(s) to determine how many leader dots to place in between the two words.

LindaKuhn_1-1660130456400.png

This is what my Arcade expression looks like:

if ($feature.Section == null)

{

return TextFormatting.NewLine + $feature.StreetNames + TextFormatting.NewLine

}

var a = $feature.StreetNames + $feature.Section

var b = Count(a)

var c = 30 - b

var d = c/2

var e = c%2

if (e == 0) {d = d +1}

var dotString = ""

for (var i=0; i<d; i++)

{dotString = dotString + ". "}

return $feature.StreetNames + " " + dotString + $feature.Section + TextFormatting.NewLine

(Note:  I'm not good with Arcade yet, so had to have a colleague help me with the above expression)

Because I have calculated the number of leader dots to have each line a total of 30 characters, I was expecting that my results would display as even straight columns ending at the same position on the right hand side, but that is not the results I'm getting.   I have tried setting my Text symbol - Appearance - Position - Horizontal alignment as "Left", "Center", "Right" and "Justify" and none of them return even columns. 

LindaKuhn_4-1660131412527.png

I have tried using several monospaced fonts (Lucida Sans Typewriter, Simsun, Courier New) and this doesn't help either.  This is the results I am getting.  The right hand side of the columns is uneven.

LindaKuhn_5-1660131450464.png

I used to make this map in ArcMap using 8 individual Text Boxes where I manually typed the street name, the leader dots and the section, hit the hard return after each line and used the full justify position.  The result was very neat looking but hard to maintain whenever new streets were built.  If the new street started with the letter "C" it would be added in the second text box in alphabetical order and then the last street from each text box to the right of it would have to be manually moved to the top of the next text box.  I'm trying to transfer this map to ArcGIS Pro and take advantage of its newer functionality but can't get results to be even on both sides of each column.  This is what the street index looks like in ArcMap using individual text boxes:

LindaKuhn_7-1660131693748.png

Any suggestions/ideas would be appreciated.

 

 

 

 

0 Kudos
1 Solution

Accepted Solutions
LindaKuhn
New Contributor II

I looked closer at your Arcade expression Line 3 and revised Line 23 to follow its formatting.  I replaced your last line of Arcade expression with:

return `\n${$feature['StreetNames']}${dot_string}${$Feature['Section']}\n`

and it looks great.  Thank you so much for your help.

LindaKuhn_1-1660145375524.png

My final Arcade Expression looks like this:

// check for section header first
if (IsEmpty($feature['Section'])){
return `\n${$feature['StreetNames']}\n`
} else {
// build string
var text_len = Count($feature['StreetNames']) + Count($Feature['Section'])
Console('total text length ', text_len)

// get number of remaining spaces between text
var spaces = 30 - text_len

var dot_string = ' '

// leaving at least two characters for spaces on either end, populate the rest of the dot string
for (var x=0; x < spaces-2; x++){
// add '.' for even-numbered iterations, ' ' for odd-numbered
dot_string += Iif(x % 2 == 0, '.', ' ')
}

dot_string += ' '

// add full string to output
return `\n${$feature['StreetNames']}${dot_string}${$Feature['Section']}\n`
}

 

 

View solution in original post

0 Kudos
7 Replies
jcarlson
MVP Esteemed Contributor

I tried accomplishing this with some sample data, and here's what worked for me:

 // check for section header first
    if (IsEmpty(s['Section'])){
        return `\n${s['StreetNames']}\n`
    } else {
        // build string
        var text_len = Count(s['StreetNames']) + Count(s['Section'])
        Console('total text length ', text_len)
        
        // get number of remaining spaces between text
        var spaces = 30 - text_len
        
        var dot_string = ' '
        
        // leaving at least two characters for spaces on either end, populate the rest of the dot string
        for (var x=0; x < spaces-2; x++){
            // add '.' for even-numbered iterations, ' ' for odd-numbered
            dot_string += Iif(x % 2 == 0, '.', ' ')
        }
        
        dot_string += ' '
        
        // add full string to output
        return `${s['StreetNames']}${dot_string}${s['Section']}\n`
    }

A few pointers:

I like to use template literals for building strings. Instead of $feature.attribute1 + ', ' + $feature.attribute2 + TextFormatting.NewLine, you could have `${feature.attribute1}, ${feature.attribute2}\n`.

For building the dot string, I did things a bit differently with the for loop. Rather than iterate over once for every two characters in the remaining space, it would take the left over characters (30 - street name and sections) and alternate between '.' and ' ', leaving room on both ends for a space.

It seems to work well enough, though the dots don't always align vertically from row to row. You could further modify this to get those dots to align nicely, but here's what my expression produces.

jcarlson_0-1660135733197.png

 

- Josh Carlson
Kendall County GIS
jcarlson
MVP Esteemed Contributor

PS - Since I was using sample data, I accessed the features with a made-up variable s. Just replace those with $feature to apply this to your own situation.

- Josh Carlson
Kendall County GIS
0 Kudos
LindaKuhn
New Contributor II

Thank you very much for responding to my post.  I took your arcade expression and replaced the variable s with $feature per your P.S.  My arcade expression looks like this:

// check for section header first
if (IsEmpty($feature['Section'])){
return `\n${$feature['StreetNames']}\n`
} else {
// build string
var text_len = Count(s['StreetNames']) + Count(s['Section'])
Console('total text length ', text_len)

// get number of remaining spaces between text
var spaces = 30 - text_len

var dot_string = ' '

// leaving at least two characters for spaces on either end, populate the rest of the dot string
for (var x=0; x < spaces-2; x++){
// add '.' for even-numbered iterations, ' ' for odd-numbered
dot_string += Iif(x % 2 == 0, '.', ' ')
}

dot_string += ' '

// add full string to output
return `${s['StreetNames']}${dot_string}${s['Section']}\n`
}

My result is only showing the headings and none of the street names

LindaKuhn_1-1660141494312.png

Did I misunderstand something?

 

0 Kudos
jcarlson
MVP Esteemed Contributor

You've still got a handful of s variables in the expression you posted.

- Josh Carlson
Kendall County GIS
0 Kudos
LindaKuhn
New Contributor II

Yes, I see that I missed a couple of variables.  Now that I've updated the Arcade expression again it looks like this:

// check for section header first
if (IsEmpty($feature['Section'])){
return `\n${$feature['StreetNames']}\n`
} else {
// build string
var text_len = Count($feature['StreetNames']) + Count($Feature['Section'])
Console('total text length ', text_len)

// get number of remaining spaces between text
var spaces = 30 - text_len

var dot_string = ' '

// leaving at least two characters for spaces on either end, populate the rest of the dot string
for (var x=0; x < spaces-2; x++){
// add '.' for even-numbered iterations, ' ' for odd-numbered
dot_string += Iif(x % 2 == 0, '.', ' ')
}

dot_string += ' '

// add full string to output
return `${Feature$['StreetNames']}${dot_string}${$Feature['Section']}\n`
}

but these revisions didn't change the end results.  I still am only getting the headings and none of the street names:

LindaKuhn_0-1660144093016.png

 

 

 

0 Kudos
LindaKuhn
New Contributor II

I looked closer at your Arcade expression Line 3 and revised Line 23 to follow its formatting.  I replaced your last line of Arcade expression with:

return `\n${$feature['StreetNames']}${dot_string}${$Feature['Section']}\n`

and it looks great.  Thank you so much for your help.

LindaKuhn_1-1660145375524.png

My final Arcade Expression looks like this:

// check for section header first
if (IsEmpty($feature['Section'])){
return `\n${$feature['StreetNames']}\n`
} else {
// build string
var text_len = Count($feature['StreetNames']) + Count($Feature['Section'])
Console('total text length ', text_len)

// get number of remaining spaces between text
var spaces = 30 - text_len

var dot_string = ' '

// leaving at least two characters for spaces on either end, populate the rest of the dot string
for (var x=0; x < spaces-2; x++){
// add '.' for even-numbered iterations, ' ' for odd-numbered
dot_string += Iif(x % 2 == 0, '.', ' ')
}

dot_string += ' '

// add full string to output
return `\n${$feature['StreetNames']}${dot_string}${$Feature['Section']}\n`
}

 

 

0 Kudos
JesseWickizer
Esri Contributor

Another approach to consider is to use a table frame for the index. There's not a specific mechanism to add dot leaders, but with some formatting of the table frame parts you could achieve a similar look without requiring the use of a monospace font.

Steps:

First, format the vertical alignment of the data text of the page number field to right justified.

Add dot leader style to table frame rows

In the table frame properties, set  Background 1 and Background 2 to 1 row each. 

Then click the symbol next to Background 1 to edit the background style. Here's where to add in a dotted style to the bottom of the text row.

JesseWickizer_1-1660931780961.png

Change the fill style to Hatched fill with 90 degree angle and about 8pt separation (adjust separation amount as you prefer). 

JesseWickizer_0-1660931545428.png

Edit the Hatch line symbol further by clicking Format line symbol... in the symbol menu.

JesseWickizer_0-1660932116930.png

Click the Structure tab (wrench symbol) then add a new Marker layer.

JesseWickizer_1-1660932224912.png

Delete the original line layer leaving only the marker layer.

Edit the marker layer properties to create the repeating dot style.

  • Size: 1pt
  • Scale proportionally: UN-check
  • Marker placement: At extremities
  • Extremities: At begin
  • Offset: 3pt

JesseWickizer_2-1660932588618.png

Now set the same style for the Background 2 style. 

 

Clean up extra dots

This also leaves dots in places we don't want, such as before and after the text and overlapping the text so the next step is to fix this.

JesseWickizer_3-1660933446205.png

Start by narrowing the space between the text and border (horizontal) in the Table frame properties. This will cover the dots hanging off the beginning and end of the text.

JesseWickizer_4-1660934091043.png

Next, edit the text symbol for the fields to add a background callout in the same color as the layout background - white in this case. I used 2px left and right margin to cover dots that are too close to the text but try other sizes that look best. 

JesseWickizer_9-1660934720551.png

Moving the table frame over a gray background reveals how it's constructed - add a white background to the whole table frame element or ensure it's paced over a white or matching background in the layout.

JesseWickizer_10-1660934900242.png

JesseWickizer_12-1660934990357.png

0 Kudos