Upload
chad-cooper
View
3.714
Download
0
Tags:
Embed Size (px)
DESCRIPTION
Poster presented at the Oklahoma South Central Arc Users Group 2008 Annual Meeting. Norman, Oklahoma, September 2008
Citation preview
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