Road digitizing with Pegasus TRK and Cyclone 3DR

Preview:

  • Road Lines

  • Curbstone

  • Poles and Signs

  • Virtual Surveyor

  • Trees

  • Buildings

  • Contours

  • Profiles

Pegasus TRK it's a new mobile scanner from Leica Geosystems, which brings autonomy, intelligence, and simplicity to mobile mapping and is set to transform mobile mapping with next-generation AI capabilities. Past few weeks I had the pleasure to work with it by myself and I can tell that it was a great time. Previously I have never worked with mobile scanners and I always thought that it is much more complicated than terrestrial scanning, but the workflow was very simple. The system always guides you and tells you what you should do next, so it works autonomously and does all work for you. But the most impressive part for me was the intelligent real-time auto-blurring algorithms that make your data secure and protected. The other side of the coin is covered by the new software for processing data - Leica Cyclone Pegasus. This software is intended to work with data from mobile sensors. It can automatically determine common points between different tracks and adjust your trajectory by itself. And one of my favorite parts is the automatic classification of point clouds, more than 20 different layers. That feature really speeds up your processing time and I decided to test that data with Cyclone 3DR for classic feature extraction purposes.

Leica Pegasus TRK500 Neo

Let's start with the road stripes extraction, for that task we can use manual and automatic approaches:

For Manual creation of polyline select the Draw Polyline tool and choose the snapping mode to White Line. The polyline will be created at the axis of white lines thanks to a seed point (Only for clouds with intensities). You have to enter a search radius (buffer area where the software will recognize white lines). You can Flip intensities (suitable when the lines are darker than the road).


If your polyline contains a too low number of points for an accurate representation of the curved lines, you can try to convert it to a spline. Click the Draw CAD Curve from the Polyline tool, this command computes CAD curve(s) according to polylines. The line will be much smoother but it will be another type of line. If you want to present it as a polyline you can convert it back with the Draw Polyline from CAD curve tool. Also, for adding a new vertex to the polyline you can hover the mouse over the blue middle sphere on the polyline and click on the appeared red arrow symbol.


For the Automatic creation of road stripes, we should filter the point cloud and segment from it points that are relevant only to the stripes. Use a different combination of filters for that task:

  • Filter by Real Color - this command splits cloud(s) according to the real color on each point. Points having a color similar to the selected one will be grouped in a point cloud.

  • Filter by intensity (Inspection Value) - This command filters points inside the cloud(s) according to a range of inspection values

  • Segment by distance - I showed this filter many times in previous tutorials, this command splits cloud(s) into a group of clouds based on the distance between points.

When you'll get a cleaned point cloud of stripes you could use simple scripts for line extraction from these clouds. For example, the Electric Line Extraction script, which tries to fit polyline into a selected cloud, usually it's using for wires extraction, but you can change the sampling step to make it more suitable for road lines. Or use the below script, it works the same way but is predefined more for roads.

Road lines script

// ------------------------ HOW TO USE IT --------------------------------------------

// 1. Select a cloud then a point to start the road line extraction.

// 2. The road line is extracted using a point on the line and a sampling step that includes the line


var step= 5;


var visibleClouds=SCloud.All(1);

if(visibleClouds.length>0){

print("click the cloud");

var cloud = SCloud.FromClick().Cloud;

print("click a seed point");

var point = SPoint.FromClick().Point;


var samplingStep = step;


var res = cloud.RegionGrowFreePolyline(point,samplingStep);

if(res.ErrorCode == 0)

{

res.Multi.SetName("Road Polyline");

res.Multi.SetColors(1.0, 0.0,0.0);

res.Multi.AddToDoc();

}

}else{

print("no clouds displayed");

}

Cyclone 3DR - Road Lines extraction

Use the same script to extract wires. Select a cloud and then a point to start the extraction. The electric line is extracted using a point on the line and a sampling step that includes the line. You could use this script to extract not only wires but almost any line object. For example some kind of fences also.

For extraction markers on the road same as for road lines, the first step should be filtering point clouds, next. we can use geometry tools to fit them in the cloud where it is possible to represent the desired elements with basic rectangle and circle objects. Also, we can use combinations of offset, transition, rotation, and other transformation tools to speed up the processing.

To extract more complex shapes use the Planar Countour command, the command allows extracting planar contours from meshes and clouds. After extraction applies the Resample Polyline tool to them and specifies desired number of vertexes. The polylines will look much better.

To represent the hardest shapes use the manual tools for creating polylines and then copy that set of polylines into other locations.

Cyclone 3DR - Road symbols

For curbstones, we also can go with manual ways and automatic ways with scripts. For manual extraction click the Draw polyline tool and set the Snap to the Ground point for extracting the Lowest line of the curb, for the highest line set the snap to the Highest point.

For the automatic way, you need to have at least one polyline that will represent the direction of the line that should be extracted. You can specify two lines (the bottom and upper parts of the curb) and then run the pre-installed Curb extraction script. The algorithm extracts the curbs at individual points defined by the sampling step. This tool works pretty well, especially with the clean and accurate point cloud from the TRK.

A few more tools that you could try for curb extraction purposes based on mesh analyses, so for them you should create a mesh from the surface and curb points. Thanks to the classification in Pegasus Office, the point cloud is already split by relevant layers. All that you need to do is select the layers with the point cloud, group them and click the Scan to Mesh tool. It's one of the easiest ways to create a mesh.

When the mesh will be created we can try to extract all lines with the Multiple Breaking Lines tool. This command offers the possibility to extract all the breaking lines (or feature lines) from a mesh. The resulting lines are divided into two sets:

  • The convex lines, in red, correspond to crest geometries,

  • The concave lines, in blue, correspond to ravine geometries.

One more unusual way to extract lines which I showed in Scan to Plan tutorial is to make a slope analysis, then segment the mesh by color and extract all borders as polylines. Anytime when we work with the automatically extracted lines, we should apply on them some filters to make them more smooth.

Cyclone 3DR - Curb Extraction Script

After the classification point cloud, we got layers with points relevant to the road signs and poles. Basically, it's just cylindrical objects and we can use the Best Fit Cylinder or Best Fit Circle tools to extract information about their geometry.

  • But before that, I prefer to segment point clouds closer to the ground, because upper points contain elements not relevant to the cylindrical objects. You can use previously created ground for that and the Separate According Distance filter. This command explodes one cloud into two parts using a selected object and the defined distance from that object. Use different distances for signs and for poles, based on their shapes. The main goal here is to get a clean point cloud of cylindrical objects.

  • Use the Segment by distance filter to split one big point cloud to separate files.

  • Next, use the Best Cylinder tool and check the One shape per input option. That tool will give you the right geometry object for all selected elements with one click.

  • I prefer also to convert these objects to mesh with the Mesh from Object tool and extract their holes and borders as polylines.

  • Then Select the lowest part of each element and project it to the ground. It will give us not only X and Y information but also Z values for each element.

If your final deliverable will be a 2D plan or 3D model in AutoCAD Civil 3D, then you can create the dynamic block with a different type of visibility and use multiple placements of this block based on information extracted from Cyclone 3DR. Select all polylines that represent cylindrical objects and apply the Best Cylinder tool to them. Set the One Shape per Input option and then use the below script to extract the center position of each geometry element.

When the position of each element will be defined, copy it from the script window, then go to Civil 3D, select your block and click Copy tool. Paste to the command line previously copied elements and you'll see that the software will insert the block to each single coordinate, that you copied previously from Cyclone 3DR.

Center point of geometry object script

//Select all Planes or Circles

//Press Play button

//Copy the output


var mySelection=SFeature.FromSel();

var oString=new String();

for(var i=0; i<mySelection.length;i++){

var centroid=mySelection[i].GetCenter();

oString+=centroid.GetX();

oString+=",";

oString+=centroid.GetY();

oString+=",";

oString+=centroid.GetZ();

oString+="";

oString+=""+ '\n';

}


print(oString);


Cyclone 3DR - extraction position of poles and export it to Civil 3D

One more useful way of creating symbols on your topographic plan is the manual extraction of particular points and assigning them description codes with the Virtual Surveyor script from my colleague Kyle Palmer. This script allows you to label picked points in 3DR in the viewing window and store the points with a feature code and comment concurrently to the selection. I prefer to select ground point snapping mode when I'm using the script, to get all points with the right Z value. Try to use the same description codes as you have in your CAD software, it'll allow you automatically generate all symbols based on their description. In Civil 3D it's called the Description Key Sets, you can add there any symbols you want and add to the special code. When you'll import the coordinates of your points with relevant codes all symbols will be generated based on these sets.

Virtual Surveyor script


//Open up a file at the user's preferred location


var pointsFile = GetSaveFileName("Choose where to save points file", '*.csv', CurrentScriptPath())

var file = SFile.New(pointsFile);



//CHECK IF THE FILE EXISTS AND GET LAST POINT NUMBER


function FileExists(checkFile)

{

// Default values if no other values found

var pointNumber = 0;

var featureCode = "FC";

var comment = "Comment";


//print("file exists")

checkFile.Open(SFile.ReadOnly);

var alllines = checkFile.ReadAll().split('\n');

if (alllines.length > 0){



var lastFeature = alllines[alllines.length-2].split(',')


checkFile.Close();


pointNumber = parseInt(lastFeature[0]) + 1;

featureCode = lastFeature[4];

comment = lastFeature[5];


}


return {

"Point": pointNumber,

"Feature_Code": featureCode,

"Comment": comment,

"_DEBUG": false


};

}




function CodePoint(point, code, comment){


var pickPoint = SPoint.FromClick().Point


pickPoint.SetName(String(point) + "-" + code)


pickPoint.AddToDoc();


pickPoint.ShowName(true);


WritePoint(pickPoint.GetName(), point, code, file, comment)


}


function WritePoint(lastPointName, lastPointNumber, lastPointCode, thefile, lastComment){


var lastPoint = SPoint.FromName(lastPointName)[0];


var filePoint = lastPointNumber

filePoint += ',' + String(lastPoint.GetY())

filePoint += ',' + String(lastPoint.GetX())

filePoint += ',' + String(lastPoint.GetZ())

filePoint += ',' + lastPointCode

filePoint += ',' + String(lastComment) + ',' + "\n";

thefile.Open(SFile.Append);


thefile.Write(filePoint);


thefile.Close();


lastPointNumber += 1;


Main(lastPointNumber, lastPointCode, lastComment)


}




function GetFeatureCode(previousPoint, previousCode, previousComment){



var myDialog = SDialog.New('Virtual Surveyor');

// Add a title

myDialog.AddLine('Set Point and Feature Code', false, { 'align': 'center', 'size': 20 });


// Add a form to fill a text

myDialog.AddLine('Point# :', true, { 'align': 'right' }, previousPoint);

// Add a form to fill a text

myDialog.AddLine('Code :', true, { 'align': 'right' }, previousCode);


// Add a form to fill a text

myDialog.AddLine('Comment :', true, { 'align': 'right' }, previousComment);

// Run the dialog

// SDialog.Execute function displays the dialog box, and launch a loop waiting when the dialog box is shutdown

// It returns 0 if the user clicks on the "OK" button

var result = myDialog.Execute();

if(result.ErrorCode == 0)

{

// Retrieve output values

var values = result.InputTbl; // InputTbl contains all the content of the input box

// to access to Comments use values[0] because it's the first input value

var pointNumber = parseInt(values[0]);


var featureCode = values[1];


var comment = values[2];


return {

"Point": pointNumber,

"Feature_Code": featureCode,

"Comment": comment,

"_DEBUG": false

};


} else {

StopWithMessage("Exiting");


}

}


function StopWithMessage(message)

{


file.Close()


var theDialog = SDialog.New('Virtual Surveyor');

theDialog.AddLine(message, false);

var result = theDialog.Execute();


//throw new Error(message);

}



function Main(lastPointNumber, lastFeatureCode, lastComment){


var Param;

var currentPoint;

var currentFeatureCode;

var currentComment;


if (file.Exists()){


var ParamExists = FileExists(file)


lastPointNumber = ParamExists.Point

lastFeatureCode = ParamExists.Feature_Code


lastComment = ParamExists.Comment


Param = GetFeatureCode(lastPointNumber, lastFeatureCode, lastComment)




} else {


Param = GetFeatureCode(lastPointNumber, lastFeatureCode, lastComment)



}


currentPoint = Param.Point


currentFeatureCode = Param.Feature_Code


currentComment = Param.Comment


CodePoint(currentPoint, currentFeatureCode, currentComment)



}



Main(0, 'FC', 'Comment');




Autodesk Civil3D - Import txt file with coordinates and description codes from Cyclone 3DR

For tree extraction, first of all, we should define how we want to see the final result: as a 2D symbol of each tree, a 2D contour of all tree crowns, or as a 3D object.

For the 2D symbols, we can use the same tools we used previously

For the 2D contour we can use the same technique as I showed in the Scan to Plan tutorial:

  • Project all tree clouds to the Ground surface

  • Analyze Mesh vs Cloud and Segment the mesh based on the analyses color

  • Subdivide the mesh and repeat the previous step

  • Extract all borders as polylines

  • Later you could use the mesh to get all surfaces

For the 3D object representation uses the Region Grow Cylinder tool to represent the tree trunk and the Convex Hull to represent the tree Crown. This command computes the convex hull of a cloud, that is to say, a mesh that contains all the points. Later you could use all generated meshes to get crown volumes.

Cyclone 3DR - Trees with Convex Hull

For buildings there are also different ways of representation, you can create a 2D plan or 3D model. In Scan to Plan tutorial, I demonstrated a lot of different workflows on how to extract 2D polylines from the cloud. Use the Scan to Plan tool and the Planar Section tool with a combination of editing polylines tools.

For the 3D model creation use the Building Extractor tool. This command allows you to create a simplified mesh based on planar parts of a point cloud or mesh.

  1. Click on the input object to extract planar areas and contours.

  2. Validate the extracted planar contour. This will create a planar mesh.

  3. Repeat both operations to create planar meshes as needed.

The connections between the extracted planes are calculated by default. However, you can force or remove some connections. To do so, all you need to do is drag one ball representing a plane onto another one to force the connection. In order to remove the connection, select the edge representing the connection and press the Delete key.

The user can manually define a plane by clicking n points (n >= 3) with the Shift key pressed. Pressing the Shift key will override the automatic plane extraction behavior. All points clicked with the Shift key pressed will be used to compute the best plane from these n points.

Cyclone 3DR - Building Extractor

One of the common tasks it's also extracting contour lines. You can do it easily if you have a ground surface with the Countour Lines tool. Just select a mesh and run the command.

  1. Define the Step between each contour line

  2. Choose the Range of the contours

  3. Set the interval between major contours

  4. Filter contours smaller than to remove irrelevant contours

Contours are created based on triangles so they could have rough shapes, to make them smoother use the Smooth Polyline tool.

If you prefer to create contours in other CAD software you can export the mesh as a LandXML file and use it in your CAD as you wish.

Cyclone 3DR - Contour Lines extraction

As additional deliverables, we can create different types of cross-sections. For the transverse profile create a trajectory first, then select it and run the Sections along Curve tool. This command computes a set of planar sections along a polyline or a curve. Specify:

  • Step of sections, you can define the step between sections and optionally a range using Custom. With a List of distances. You can type distances and/or click points in the scene.

  • Searching distance to draw the sections only in the curve neighborhood. The value corresponds to a radius.

After creation, you can resample transverse polylines as you wish.

For the longitudinal profile, you can use the below script. This script provides functionality to unroll a 3D polyline so as to plot a longitudinal profile.

  1. Create a sample polyline to represent the trajectory of the road.

  2. Project it on mesh with the Projection tool

  3. Run the script and select the projected polyline

The script will create the longitudinal profile, project it vertically on the horizontal plane, and compare both lines. Later you can disable the comparison if you want to leave only the profile polyline on the screen.

Longitudinal profile

//Launch the script and click on the polyline

var myAxis = SMultiline.FromClick();

//Project vertically the 3D polyline on a horizontal plane (Z=0)

var zenith = SVector.New(0, 0, 1)

var hzPlane = SPlane.New(SPoint.New(0, 0, 0), zenith);

var hzAxis = hzPlane.ProjDir(myAxis.Multi, zenith);

//Compute the height between 2D and 3D polylines

var inspectedHzAxis = hzAxis.Multi.Compare(myAxis.Multi, 0, zenith, 1, false, true);

//Create the longitudinale profile

var res = SMultiline.New();

var lineRef = SMultiline.New();

for (var i = 0; i < inspectedHzAxis.Multi.GetNumber(); i = i + 1) {

//Compute abscissa

var cutPoint = inspectedHzAxis.Multi.GetPoint(i);

var bothEnds = SMultiline.Cut(inspectedHzAxis.Multi, cutPoint);

var x = bothEnds.MultiTbl[0].GetLength();

//Create points of longitudinal profile and reference line

var z = inspectedHzAxis.Multi.GetDeviation(i);

var myPoint = SPoint.New(x, 0, z);

var myRefPoint = SPoint.New(x, 0, 0);

res.InsertLast(myPoint, 0);

lineRef.InsertLast(myRefPoint, 0);

}

var lp = res.Compare(lineRef, 0, zenith, 1, false, true);

//Insert longitudinal profile and reference line

lp.Multi.AddToDoc();

lineRef.AddToDoc();

Cyclone 3DR - Transverse and Longitudinal profiles

If you still have a question or you want to repeat this tutorial by yourself, but don't have a valid license then you could reach me by clicking the below button.