Skip to content

String Styles

The StringStyleConfig file controls how each string is drawn — its CAD layer, linetype, draw action, and any additional geometry. Strings are the lines, polylines, or 3D polylines that connect points of the same code and attribute group. The geometry type is set by the Actions value for that code.

The file is located at: %APPDATA%\SpatialViz\StringStyles\<ConfigName>_StringStyleConfig.txt

Open it from R3MANAGECONFIGS → Edit String Styles.

File format

The file has three parts:

  1. A global default — applied to all strings that have no other rule
  2. Group blocks — named groups of codes sharing the same layer
  3. Per-code blocks — individual code overrides with optional conditionals and actions

Global default

default => LineType=ByLayer, Actions=Line()

All strings that have no other matching rule use ByLayer linetype and are drawn as connected line segments (Line()). Adjust this if you want a different baseline.

Group blocks

group DRAINAGE
  include = BC, CHNL, DD, DP, GGP, GR, HW, LN, PI, RAP, SWP, WEIR
  Layer   = DRAINAGE

All codes in include are drawn on the DRAINAGE layer. This is the most common way to organise string layers.

Property Meaning
group Group name (internal only — not written to CAD)
include Comma-separated list of codes belonging to this group
Layer The CAD layer for strings in this group
LineType Optional linetype override (default: ByLayer)
Actions Optional draw action override (default: Line())

Long include lists can wrap across indented lines — continuation is implied while the line remains indented:

group ELECTRICAL
  include = CAB1, CAB2, CAB3, CABINET, ECABLE, EMARK, EPIT, LIGHT, LIGHT3, LIGHTR, PILLAR, POLE
            PYLON, SUB, SWITCH
  Layer   = ELECTRICAL

Per-code blocks

When a single code needs its own layer, linetype, specific actions, or conditional behaviour, write a named block directly (not inside a group):

CODENAME
  Layer    = LAYERNAME
  LineType = LINETYPENAME
  Actions  = Line()

Conditional rules

Use LayerIf, LineTypeIf, and ActionIf to change settings based on attribute values. Conditions use the same syntax as PointStyleConfig:

CODENAME
  LayerIf    A3 = Top    => TOPOGRAPHY-TOP
  LayerIf    A3 = Bottom => TOPOGRAPHY-BOT
  LineTypeIf A3 = Top    => DASHED
  ActionIf   A3 = Top    => Polyline()
  ActionElse              = Line()

Rules are evaluated top-to-bottom; the first match wins for each rule kind (LayerIf, LineTypeIf, ActionIf).

Use && for AND conditions:

KERB
  LayerIf A3 = Top && A5 = Mountable => RD-KB-TOP
  LayerIf A3 = Top                   => RD-KB-TOP
  LayerIf A3 = Bottom                => RD-KB

Use != to match when an attribute does not equal a value:

KERB
  ActionIf A3 != Bottom => 3DPolyline()
  ActionElse             = Line()

ActionElse is the fallback when no ActionIf matches.

Precedence

Rules are applied in this order — later entries win over earlier:

  1. Global default
  2. Group block (provides layer and default actions for the code)
  3. Per-code block (most specific — overrides group and global)

Within a per-code block, conditional rules are evaluated top-to-bottom, first match wins per rule kind.

String actions

Actions control the geometry drawn for the string. Each action listed below can be used in Actions =, ActionIf ... =>, or ActionElse =.

Baseline geometry

These actions draw the primary linework connecting the string's points.

Line() or Line(Dim=2D)

Draws the string as individual AutoCAD line segments between consecutive points.

Actions = Line()          # 3D lines (default)
Actions = Line(Dim=2D)    # flatten to 2D (Z = 0)

Polyline()

Draws the string as a single 2D lightweight polyline.

Actions = Polyline()

3DPolyline()

Draws the string as a single 3D polyline, preserving all point elevations.

Actions = 3DPolyline()

Note

Polyline() and 3DPolyline() are mutually exclusive with Line(). If either is present, Line() is suppressed automatically to prevent double-drawing.


Offset(DIST, ...)

Draws a parallel offset copy of the string at the specified distance.

Offset(DISTANCE, VariableMode=Transition, EraseSource=false, Caps="condition")
Parameter Description
DISTANCE Offset distance in drawing units; can be a fixed number or attribute expression like {A4}. Use a negative value to offset to the opposite side.
VariableMode How to handle variable-width strings: Transition (interpolate), First, Last, Mean
EraseSource true removes the original baseline string after offsetting (default: false)
Caps Attribute condition (same syntax as TargetWhere) — draws a short line segment from each matching point to its corresponding vertex on the offset polyline, closing the wall end. Used for corner shots on walls and fences.
# Fixed 0.5 m offset
Actions = Line(), Offset(0.5)

# Variable offset from A4, erasing the source
ActionIf A4 = * => Offset({A4}, EraseSource=true)

# Wall — offset width from A7, with cap lines at corner shots
WALL
  Actions = Polyline()
  ActionIf A6 = Right of String => Offset({A7}, Caps="A4=Corner")
  ActionIf A6 = Left of String  => Offset({A7}*-1, Caps="A4=Corner")

Cap lines are drawn from the surveyed corner point to the corresponding vertex on the offset polyline. This closes the wall end at corners without requiring a separate survey shot at the offset position.


AddArc(EraseSource=true|false)

Fits circular arcs through sliding triplets of consecutive points. Used when field crews record intermediate arc shots (three points define an arc).

Actions = AddArc()                    # keep source lines
Actions = AddArc(EraseSource=true)    # replace source lines with arcs

When EraseSource=true, the baseline line segments between arc triplets are removed and replaced by the fitted arcs.

Use with ActionIf to limit arc fitting to specific attribute conditions:

KERB
  ActionIf A3 = Arc => AddArc(EraseSource=true)
  ActionElse         = Line()

AddCircle(EraseSource=true|false)

Draws a circle through exactly three points. The three points must not be collinear.

Actions = AddCircle()                 # keep source lines
Actions = AddCircle(EraseSource=true) # remove source lines

If the three-point circle cannot be formed (collinear, or wrong point count), the action is silently skipped and the baseline is drawn instead.


AddRectangle(Dim=2D|3D, ZMode=..., BestFit=true)

Draws a rectangle through 3 or 4 surveyed points. Useful for structures recorded as corner shots.

Actions = AddRectangle()
Actions = AddRectangle(Dim=2D, ZMode=Mean)
Actions = AddRectangle(BestFit=true)
Parameter Description
Dim 2D (flatten Z) or 3D (preserve elevations). Default: 3D
ZMode How to compute the Z of rectangle vertices: Plane (best-fit plane), Mean (average), Zero, EndPoint, Grade. Default: Plane
BestFit true fits a least-squares rectangle with exactly 90-degree corners. Default: false (parallelogram completion)

The baseline linework is suppressed when AddRectangle draws successfully.

Standard vs BestFit

Standard (default): For 3 points A, B, C — computes the fourth point D as A + C − B (parallelogram completion). The resulting shape is only as square as the survey. For 4 points: closes the shape as-is.

BestFit (BestFit=true): Finds the rectangle with exactly 90-degree corners that minimises the sum of squared distances from the surveyed points to the rectangle corners. Use this when the structure is known to be square but the survey is imprecise — for example chimneys, stormwater pits, columns, or any structure where it is difficult to get a perfect right-angle shot in the field.

  • 3 points: tries each point in turn as the right-angle pivot corner. For each candidate, finds the orientation that analytically minimises the squared perpendicular deviations of the two adjacent points from their axes. The configuration with the lowest total residual is used.
  • 4 points: uses principal component analysis (PCA) on the point cloud to determine the rectangle's primary axis, then fits the bounding rectangle.
# Chimney — best-fit rectangle, always exactly square
CHIM
  Actions = AddRectangle(BestFit=true, Dim=2D)

# Stormwater pit — 3 or 4 shots, ZMode=Mean
PIT
  Actions = AddRectangle(BestFit=true, ZMode=Mean)

Z values for the four computed corners are derived using ZMode against a plane fitted to all input points.


AddText(TEXT, ...)

Places text labels at the midpoints of qualifying string segments.

AddText("TEXT", Layer=LAYERNAME, Mode=EachSeg, Size=2.5)
Parameter Description
TEXT The label string; use {A1}{A8} placeholders
Layer CAD layer for the text entity (defaults to the string's layer)
Mode Which segments receive a label — see table below
Size Text height in mm on the plotted sheet

Mode values:

Mode Behaviour
EachSeg Label every qualifying segment (default)
Pairs Label only the first and last segment of each qualifying run
Isolated Label only segments with no qualifying neighbours on either side
FirstOnly Label only the first qualifying segment
# Label every segment of a FENCE string with the fence type
FENCE
  Actions  = Line()
  AddText("{A2} FENCE", Layer=FENCES_LABELS, Mode=Pairs)

# Label only segments where A3 = Gate
FENCE
  ActionIf A3 = Gate => AddText("GATE", Layer=FENCES_LABELS, Mode=Isolated)
  Actions             = Line()

Placeholders {A1}{A8} are expanded using the attributes of the segment's endpoints.


IntersectWith(...)

Extends or trims a string's endpoints to meet another nearby string of the same code. The endpoint adjustments are pre-computed before drawing so draw order does not matter.

IntersectWith(Within=N, Nearest=Mutual, TargetWhere="A3 = Value", AngleMinDeg=30, Dim=3D, ZMode=Plane)
Parameter Default Description
Within Required. Search radius in drawing units
Nearest Mutual Which end to extend: Mutual (both ends), Source (this string), Target (the other string)
TargetWhere any Filter target strings by attribute conditions (see below)
AngleMinDeg 30 Minimum intersection angle in degrees — shallow intersections are ignored
Dim 3D 2D or 3D
ZMode Plane How to compute Z at the intersection: Plane, Mean, Zero, EndPoint, Grade

IntersectWith only considers strings of the same code as the source string. You cannot use it to intersect across different codes (e.g. WALL into KERB).

TargetWhere — attribute filtering

TargetWhere filters which target string endpoints are eligible for intersection. It uses A1A8 attribute key=value expressions, the same syntax as ActionIf conditions:

TargetWhere="A3 = Line only"
TargetWhere="A3 = Line only && A5 = Ground"

Use && for AND conditions. TargetWhere does not accept a bare code name — it only works with key = value pairs.

Attribute placeholders {A1}{A8} are expanded from the source end's attributes, allowing the filter to match targets with the same attribute value as the source:

TargetWhere="A3 = Line only && A5 = {A5}"

This matches targets where A3 = Line only and A5 equals whatever A5 is on the source end — useful when a code has multiple variants (e.g. ground floor vs upper floor) that should only intersect within the same level.

Subject end filtering: TargetWhere is also applied to the source endpoint itself. If the source endpoint does not satisfy the condition, it will not be extended. This means a string that ends with a Corner-coded point will not have that end extended, even if other points in the same string are Line only.

# Buildings — only extend Line only ends, never Corner ends
BLD
  LayerIf A5 = Ground => BUILDINGS_G
  LayerIf A5 = L1     => BUILDINGS_L1
  ActionIf A3 = Line only => IntersectWith(Within=1.5, AngleMinDeg=30, Dim=3D, ZMode=Grade, TargetWhere="A3 = Line only && A5 = {A5}")

In this example: - Only BLD strings with at least one A3 = Line only point trigger the action - Only endpoints where A3 = Line only are extended (Corner endpoints are skipped) - Only targets where A3 = Line only and A5 matches the source level are eligible - Strings on different levels (e.g. Ground vs L1) never intersect with each other


Full example (from R3Survey2025 configuration)

This is the complete StringStyleConfig for the R3Survey2025 configuration. It uses only group blocks — the most common approach.

default => LineType=ByLayer, Actions=Line()

group AIRCON
  include = ACON, ADUCT
  Layer   = AIRCON

group BALCONIES
  include = BAL
  Layer   = BALCONIES

group BUILDINGS
  include = BAL, BLD, CE, COL, FFL, FL, GU, OH, RI, ROOM, VER
  Layer   = BUILDINGS

group BUILD-ELEVS
  include = WI, DO
  Layer   = BUILD-ELEVS

group DRAINAGE
  include = BC, CHNL, DD, DP, GGP, GR, HW, LN, PI, RAP, SWP, WEIR
  Layer   = DRAINAGE

group ELECTRICAL
  include = CAB1, CAB2, CAB3, CABINET, ECABLE, EMARK, EPIT, LIGHT, LIGHT3, LIGHTR, PILLAR, POLE
            PYLON, SUB, SWITCH
  Layer   = ELECTRICAL

group FENCES
  include = FCE
  Layer   = FENCES

group VEGETATION
  include = TR, TREE
  Layer   = VEGETATION

Per-code block examples

The following examples show the per-code block syntax for codes that need more control than a group block provides:

# Kerb — separate layers for top and bottom, 3D polyline
KERB
  LayerIf A3 = Top    => RD-KB-TOP
  LayerIf A3 = Bottom => RD-KB
  Actions = 3DPolyline()

# Building outline — draw as offset pair (wall thickness from A6) and arc where marked
BLD
  Layer   = BUILDINGS
  Actions = Line()
  ActionIf A7 = Arc => AddArc(EraseSource=true)
  Offset({A6})

# Drainage pipe — label each segment with diameter and material
PI
  Layer   = DRAINAGE
  Actions = Line()
  AddText("{A3}%%c {A4} PIPE", Layer=DRAINAGE_LABELS, Mode=Pairs, Size=2.2)

Notes

  • A code can appear in multiple groups — it is drawn on the last matching group's layer
  • Codes not listed in any group or per-code block fall back to the global default
  • Linetype ByLayer inherits from the CAD layer definition — set layer linetypes in the drawing template
  • String layers must already exist in the drawing or template — R3Survey does not create missing layers
  • AddRectangle, AddCircle, and AddArc suppress baseline linework when they draw successfully

Relationship to PointStyleConfig

StringStyleConfig controls the string (line, polyline, or 3D polyline); PointStyleConfig controls each point independently. A point and its strings can be on different layers.

For example, KERB points are placed on KERB-PNTS (by PointStyleConfig), while the kerb string is placed on RD-KB (by StringStyleConfig).