+ All Categories
Transcript
Page 1: Advanced Labeling In ArcMap With VBScript FindLabel Functions

Have you ever needed to do just a little bit or perhaps much

more to label a featureclass in ArcMap than the standard

ESRI or even Maplex labeling engine would allow? Me too.

Lucky for us, with the release of ArcGIS 8.1, ESRI

introduced "advanced" labeling to all licensing levels using

the FindLabel function and your choice of two scripting

languages: VBScript and JScript. Advanced labeling along

with the FindLabel function provides a way to

programmatically define the text that displays when labeling

features. Through the use of code examples, we will focus

on using VBScript and FindLabel functions to do advanced

labeling of features by incorporating scripting functionality

such as conditional logic, arrays, and regular expressions.

With a little (or sometimes a lot) of code, users can either

simplify or add complexity to their feature labels without

having to modify the underlying data, create temporary

datasets for the sole purpose of labeling, or convert labels

to annotation and - oh, the horror - manually arrange labels

by hand.

Inspirations/other resources

Hankley, Chip. Using VBScript to Build Complex Labels in

ArcGIS. ArcUser. October-December 2004: 50-53. Online

at http://www.esri.com/news/arcuser/1104/files/

vbscript_label.pdf

Hoque, Mohammed A. Labeling Features Using Attributes

from an External Database. ArcUser July-September 2005:

52-53. Online at http://www.esri.com/news/arcuser/0705/

files/externaldb.pdf

exists

This poster available as a pdf at: http://www.super-cooper.com/okscaug

1 Function FindLabel ([TOTNETAC], [NETNP], [NETPR], [NETHBP], [NETHBO], _

2 [TOTNETFED], [NETFEDNP], [NETFEDPR], [NETFEDHBP], [NETFEDHBO])

3

4 Dim iMaxValSz, h, i, j, k, space, intIndex, fedIntIndex, netArray, strLabel, strCarReturns, _

5 strFedLabel, mathNP, mathHBP, mathPR

6 iMaxValSz = 0 ' set max value size initial value to zero7 ' Set our field values into variables8 totnetac = [TOTNETAC]

9 netnp = [NETNP]

10 netpr = [NETPR]

11 nethbp = [NETHBP]

12 nethbo = [NETHBO]

13 fednp = [NETFEDNP]

14 fedpr = [NETFEDPR]

15 fedhbp = [NETFEDHBP]

16 ' SECTIONS WITH FEDERAL LEASES17 If FormatNumber([TOTNETFED]) > 0 Then

18 ' Do some math, compare fed to non‐fed NP/HBP/PR, if arent equal, get the difference 19 ' between the two, the non‐federal portion. If equal, set var to zero. Var goes into20 ' array, if zero, ignore it, if non‐zero, display as non‐federal acreage21 ' Compare NP/FNP22 If [NETNP] = [NETFEDNP] Then

23 mathNP = "0"24 Else

25 If ([NETNP] ‐ [NETFEDNP]) <= 0.5 Then

26 mathNP = FormatNumber(([NETNP] ‐ [NETFEDNP]),2)

27 ElseIf Right((FormatNumber(([NETNP] ‐ [NETFEDNP]),2)),2) = "00" Then

28 mathNP = FormatNumber(([NETNP] ‐ [NETFEDNP]),0)

29 ElseIf Right((FormatNumber(([NETNP] ‐ [NETFEDNP]),2)),2) <> "00" Then

30 mathNP = FormatNumber(([NETNP] ‐ [NETFEDNP]),2)

31 End If

32 End If

33 ' Compare HBP/FHBP34 If [NETHBP] = [NETFEDHBP] Then

35 mathHBP = "0"36 Else

37 If ([NETHBP] ‐ [NETFEDHBP]) <= 0.5 Then

38 mathHBP = FormatNumber(([NETHBP] ‐ [NETFEDHBP]),2)

39 ElseIf Right((FormatNumber(([NETHBP] ‐ [NETFEDHBP]),2)),2) = "00" Then

40 mathHBP = FormatNumber(([NETHBP] ‐ [NETFEDHBP]),0)

41 ElseIf Right((FormatNumber(([NETHBP] ‐ [NETFEDHBP]),2)),2) <> "00" Then

42 mathHBP = FormatNumber(([NETHBP] ‐ [NETFEDHBP]),2)

43 End If

44 End If

45 ' Compare PR/FPR46 If [NETPR] = [NETFEDPR] Then

47 mathPR = "0"48 Else

49 If ([NETPR] ‐ [NETFEDPR]) <= 0.5 Then

50 mathPR = FormatNumber(([NETPR] ‐ [NETFEDPR]),2)

51 ElseIf Right((FormatNumber(([NETPR] ‐ [NETFEDPR]),2)),2) = "00" Then

52 mathPR = FormatNumber(([NETPR] ‐ [NETFEDPR]),0)

53 ElseIf Right((FormatNumber(([NETPR] ‐ [NETFEDPR]),2)),2) <> "00" Then

54 mathPR = FormatNumber(([NETPR] ‐ [NETFEDPR]),2)

55 End If

56 End If

57 ' Build array for FED values, if mathNP/mathHBP/mathPR are zero, theyre ignored below when58 ' tested, otherwise, they are included in the array explosion as non‐fed acreage59 fedArray = Array(Array(totnetac, "<clr red='255'>", " CNT</clr>"), _60 Array(mathNP, "<clr red='0' green='0' blue='0'>", " NP</clr>"), _61 Array(mathHBP, "<clr red='0' green='0' blue='0'>", " HBP</clr>"), _62 Array(mathPR, "<clr red='0' green='0' blue='0'>", " PR</clr>"), _63 Array(fednp, "<clr red='132' green='0' blue='168'>", " FNP</clr>"), _64 Array(fedpr, "<clr red='132' green='0' blue='168'>", " FPR</clr>"), _65 Array(fedhbp, "<clr red='132' green='0' blue='168'>", " FHBP</clr>"))66 ' Determine length of longest acreage string in array67 ' Use it later to center longer strings in section68 For i = 0 To UBound(fedArray)

69 j = fedArray(i)

70 If (Len(j(1)) > iMaxValSz) Then

71 iMaxValSz = Len(j(1))

72 End If

73 Next

74 ' START MAKING LABEL75 strFedLabel = "<bol>"76 ' Explode array values, if they are > 077 k = fedArray(0)

78 fedIntIndex = 0

79 For h = 0 To UBound(fedArray)

80 k = fedArray(h)

81

82 If iMaxValSz > 2 Then ' push longer acreage strings over to 83 space = " " ' the left a little, center in section84 Else

85 space = ""86 End If

87

88 If k(0) > 0 Then

89 strFedLabel = strFedLabel & space & k(1) & k(0) & k(2) & vbNewLine

90 fedIntIndex = fedIntIndex + 1 ' count lines for non‐zero hits from array91 End If

92 Next

93 ' Determine how many carriage returns are needed to top align94 ' acreage list in the section polygon, based on line hits above95 Select Case fedIntIndex

96 Case 2

97 strCarReturns = vbNewLine & vbNewLine

98 Case 3

99 strCarReturns = vbNewLine

100 Case Else

101 strCarReturns = ""102 End Select

103 ' FINAL BUILD OF LABEL104 FindLabel = strFedLabel & "</bol>" & strCarReturns

105

106 Else

107 ' SECTIONS WITHOUT FEDERAL LEASES108 ' Build nested array of field values for non‐federal acreage109 netArray = Array(Array(" CNT</clr>", totnetac, "<clr red='255'>"), _110 Array(" NP</clr>", netnp, "<clr red='0' green='0' blue='0'>"), _111 Array(" PR</clr>", netpr, "<clr red='0' green='0' blue='0'>"), _112 Array(" HBP</clr>", nethbp, "<clr red='0' green='0' blue='0'>"), _113 Array(" HBO</clr>", hbo, "<clr red='0' green='0' blue='0'>"))114 ' Determine length of longest acreage string in array115 ' Use it later to center longer strings in section116 For i = 0 To UBound(netArray)

117 j = netArray(i)

118 If (Len(j(1)) > iMaxValSz) Then

119 iMaxValSz = Len(j(1))

120 End If

121 Next

122 ' START BUILDING THE LABEL123 strLabel = "<bol>"124 ' Loop thru array, get our values, if not = 0125 j = netArray(0) ' reset j to be first sub‐array in netArray126 intIndex = 0 ' reset array counter127 For i = 0 To UBound(netArray)

128 j = netArray(i)

129

130 If iMaxValSz > 2 Then ' push longer acreage strings over to 131 space = " " ' the left a little, center in section132 Else

133 space = ""134 End If

135

136 If j(1) > 0 Then ' test for zero values, skip em137 strLabel = strLabel & space & j(2) & j(1) & j(0) & vbNewLine

138 intIndex = intIndex + 1 ' count how many returns we get139 End If ' from our array, only non‐zero hits140 Next

141 ' Determine how many carriage returns are needed to top align142 ' acreage list in the section polygon, based on line hits above143 Select Case intIndex

144 Case 2

145 strCarReturns = vbNewLine & vbNewLine

146 Case 3

147 strCarReturns = vbNewLine

148 Case Else

149 strCarReturns = ""150 End Select

151 ' FINAL BUILD OF NON‐FEDERAL LABEL152 FindLabel = strLabel & strFedLabel & "</bol>" & strCarReturns

153 End If

154 End Function

The Problem

You have a dataset with multiple attributes

you want to label with, but when values are

absent (zero or null), you want to skip that

attribute. Also, it’s a polygon featureclass,

so absolutely positioning the labels can be

difficult.

1 Function FindLabel ([TOTNETAC],[NETPR],[NETNP],[NETHBP],

[NETHBO],[NETFEDPR],[NETFEDNP],[NETFEDHBP])

2 FindLabel = [TOTNETAC] & vbNewLine & [NETPR] & vbNewLine & _

3 [NETNP] & vbNewLine & [NETHBP] & vbNewLine & _

4 [NETHBO] & vbNewLine & [NETFEDPR] & vbNewLine & _

5 [NETFEDNP] & vbNewLine & [NETFEDHBP]

6 End Function

Better Solution

Arrays to the rescue! Using a nested array,

we can test values and only use valid ones in

our label. We can also color the fonts of

specific attributes, perform calculations using

attribute values and place the results into our

label array. By using a point featureclass

(converted from the polygon featureclass) to

label with, we have control over the absolute

position of the labels around the points.

Partial Solution

Sure, we can stack the labels with a simple

FindLabel function, but the results aren’t pretty at all.

Values of zero

get omitted

from the array

1 Function FindLabel ( [Well_Name] )

2

3 FindLabel = ParseWellName([Well_Name])

4

5 End Function

6

7 Function ParseWellname(well_name)

8

9 Dim patt, reg_exp, repl

10 Set reg_exp = New RegExp

11 reg_exp.IgnoreCase = False

12 ' Regex to look for commingle, tubing, casing, inactive,13 ' not active zones that we don't want labeled 14 patt = "(\s)" & _

15 "(C*\s*T*\s*\(?CM\)?\s*(\(Inactive\)$|\(Not Active\))*$|" & _

16 "C\s*(\(Inactive\)$|\(Not Active\))*$|" & _

17 "T\s*(\(Inactive\)$|\(Not Active\))*$|" & _

18 "UT \(Inactive\)$|" & _

19 "LT \(Inactive\)$|" & _

20 "(\(Inactive\)$|\(Not Active\))*$|" & _

21 "C$|T$|UT$|LT$|" & _

22 "(\(?CM\)?$|T|C)* \(?CM\)?$|" & _

23 "[C|CM|T|LT|UT]*\s*STORAGE$)"24

25 reg_exp.Pattern = patt

26 repl = ""27

28 result = reg_exp.Replace(well_name, repl)

29

30 ParseWellname = result

31

32 End Function

The Problem

Here we have a point dataset (gas wells, to be more precise) where multiple

points in the same location are stacked and each one has a different label. Each

record for one well (some have only one point, some have 4+) represents a

producing zone. The well names are almost identical except for codes that denote

what zone the well is producing from. We need to get rid of the multiple labels, yet

still show all of the points.

The Solution

Let’s put a regular expression to work for us. Regular expressions

provide us with a way of identifying characters, words, or patterns

of characters in strings of text. They are similar to wildcards,

except much more powerful. For our label to work, you must have

access to the Maplex labeling engine, since we are going to use

the “Remove duplicate labels” function of the Maplex engine.

Here’s how this works:

a) Look at the attribute table for this dataset (upper right). Notice

that well names are all based off of the lease name and have the

zone information appended to that. If we can make the well names

all the same, then the Maplex “Remove duplicates” function will

remove all but one of those labels – with no modification of our

underlying dataset!

b) Our regular expression looks for the common combinations that

occur in the zone names at the end of the well names (“CM”, “UT”,

“Not Active”, etc.) and simply replaces those occurrences with “” -

nothing.

c) Now that our labels are all the same for any one well with

multiple zones, Maplex drops the duplicates and leaves us just one

– exactly what we wanted.

For more information about using regular expressions with VBScript, see http://msdn.microsoft.com/en-us/library/ms974570.aspx or Google “regular expressions vbscript”.

I also find the book VBScript in a Nutshell (published by O’Reilly, ISBN# 0596004885) to be an indispensable resource for writing FindLabel functions in VBScript.

2 Place it in the

label expression

4 Set duplicate label

search tolerance

1 Write the FindLabel

function

3 Remove duplicate

labels

1 Write the FindLabel

function

2 Place it in the

label expression

3 Absolutely position

label around point

BEFOREBEFORE

AFTER

AFTER

DISCLAIMER: Not the

most efficient or elegant

regular expression, but

it works

Top Related