Use of this function requires a license for Whitebox Workflows for Python Professional (WbW-Pro). Please visit www.whiteboxgeo.com to purchase a license.
The ModifyLidar tool can be used to alter the properties of points within a LiDAR point cloud. The user provides a statement (statement
) containing one or more expressions, separated by semicolons (;). The expressions are evaluated for each point within the input LiDAR file (input
). Expressions assign altered values to the properties of points in the output file (output
), based on any mathematically defined expression that may include the various properties of individual points (e.g. coordinates, intensity, return attributes, etc) or some file-level properties (e.g. min/max coordinates). As a basic example, the following statement:
x = x + 1000.0
could be used to translate the point cloud 1000 x-units (note, the increment operator could be used as a simpler equivalent, x += 1000.0
).
Note that if the user does not specify the optional input LiDAR file, the tool will search for all valid LiDAR (.las, .laz, *.zlidar) files contained within the current working directory. This feature can be useful for processing a large number of LiDAR files in batch mode. When this batch mode is applied, the output file names will be the same as the input file names but with a '_modified' suffix added to the end.
Expressions may contain any of the following point-level or file-level variables:
Variable Name | Description | Type | |
**Point-level properties** | |||
x | The point x coordinate | float | |
y | The point y coordinate | float | |
z | The point z coordinate | float | |
xy | An x-y coordinate tuple, (x, y) | (float, float) | |
xyz | An x-y-z coordinate tuple, (x, y, z) | (float, float, float) | |
intensity | The point intensity value | int | |
ret | The point return number | int | |
nret | The point number of returns | int | |
is_only | True if the point is an only return (i.e. ret == nret == 1), otherwise false | Boolean | |
is_multiple | True if the point is a multiple return (i.e. nret > 1), otherwise false | Boolean | |
is_early | True if the point is an early return (i.e. ret == 1), otherwise false | Boolean | |
is_intermediate | True if the point is an intermediate return (i.e. ret > 1 && ret < nret), otherwise false | Boolean | |
is_late | True if the point is a late return (i.e. ret == nret), otherwise false | Boolean | |
is_first | True if the point is a first return (i.e. ret == 1 && nret > 1), otherwise false | Boolean | |
is_last | True if the point is a last return (i.e. ret == nret && nret > 1), otherwise false | Boolean | |
class | The class value in numeric form, e.g. 0 = Never classified, 1 = Unclassified, 2 = Ground, etc. | int | |
is_noise | True if the point is classified noise (i.e. class == 7 | class == 18), otherwise false | Boolean |
is_synthetic | True if the point is synthetic, otherwise false | Boolean | |
is_keypoint | True if the point is a keypoint, otherwise false | Boolean | |
is_withheld | True if the point is withheld, otherwise false | Boolean | |
is_overlap | True if the point is an overlap point, otherwise false | Boolean | |
scan_angle | The point scan angle | int | |
scan_direction | True if the scanner is moving from the left towards the right, otherwise false | Boolean | |
is_flightline_edge | True if the point is situated along the filightline edge, otherwise false | Boolean | |
user_data | The point user data | int | |
point_source_id | The point source ID | int | |
scanner_channel | The point scanner channel | int | |
time | The point GPS time, if it exists, otherwise 0 | float | |
rgb | A red-green-blue tuple (r, g, b) if it exists, otherwise (0,0,0) | (int, int, int) | |
nir | The point near infrared value, if it exists, otherwise 0 | int | |
pt_num | The point number within the input file | int | |
**File-level properties (invariant)** | |||
n_pts | The number of points within the file | int | |
min_x | The file minimum x value | float | |
mid_x | The file mid-point x value | float | |
max_x | The file maximum x value | float | |
min_y | The file minimum y value | float | |
mid_y | The file mid-point y value | float | |
max_y | The file maximum y value | float | |
min_z | The file minimum z value | float | |
mid_z | The file mid-point z value | float | |
max_z | The file maximum z value | float | |
x_scale_factor | The file x scale factor | float | |
y_scale_factor | The file y scale factor | float | |
z_scale_factor | The file z scale factor | float | |
x_offset | The file x offset | float | |
y_offset | The file y offset | float | |
z_offset | The file z offset | float |
Most of the point-level properties above are modifiable, however some are not. The complete list of modifiable point attributes include, x, y, z, xy, xyz, intensity, ret, nret, class, user_data, point_source_id, scanner_channel, scan_angle, time, rgb, nir, is_synthetic, is_keypoint, is_withheld, and is_overlap. The immutable properties include is_only, is_multiple, is_early, is_intermediate, is_late, is_first, is_last, is_noise, and pt_num. Of the file-level properties, the modifiable properties include the x_scale_factor, y_scale_factor, z_scale_factor, x_offset, y_offset, and z_offset.
In addition to the point properties defined above, if the user applies the lidar_eigenvalue_features tool on the input LiDAR file, the modify_lidar tool will automatically read in the additional *.eigen file, which include the eigenvalue-based point neighbourhood measures, such as lambda1
, lambda2
, lambda3
, linearity
, planarity
, sphericity
, omnivariance
, eigentropy
, slope, and residual
. See the lidar_eigenvalue_features documentation for details on each of these metrics describing the structure and distribution of points within the neighbourhood surrounding each point in the LiDAR file.
Expressions may use any of the standard mathematical operators, +, -, *, /, % (modulo), ^ (exponentiation), comparison operators, <, >, <=, >=, == (equality), != (inequality), and logical operators, && (Boolean AND),
(Boolean OR). Expressions must evaluate to an assignment operation, where the variable that is assigned | to must be a modifiable point-level property (see table above). That is, expressions should take the form
pt_variable = ...
. Other assignment operators are also possible (at least for numeric non-tuple properties), such as the increment (=+) operator (e.g. x += 1000.0
) and the decrement (-=) operator (e.g. y -= 1000.0
). Expressions may use a number of built-in mathematical functions, including:
Function Name | Description | Example |
if | Performs an if(CONDITION, TRUE, FALSE) operation, return either the value of TRUE or FALSE depending on CONDITION | `ret = if(ret==0, 1, ret)` |
abs | Returns the absolute value of the argument | `value = abs(x - mid_x)` |
min | Returns the minimum of the arguments | `value = min(x, y, z)` |
max | Returns the maximum of the arguments | `value = max(x, y, z)` |
floor | Returns the largest integer less than or equal to a number | `x = floor(x)` |
round | Returns the nearest integer to a number. Rounds half-way cases away from 0.0 | `x = round(x)` |
ceil | Returns the smallest integer greater than or equal to a number | `x = ceil(x)` |
clamp | Forces a value to fall within a specified range, defined by a minimum and maximum | `z = clamp(min_z+10.0, z, max_z-20.0)` |
int | Returns the integer equivalent of a number | `intensity = int(z)` |
float | Returns the float equivalent of a number | `z = float(intensity)` |
to_radians | Converts a number in degrees to radians | `val = to_radians(scan_angle)` |
to_degrees | Converts a number in radians to degrees | `scan_angle = int(to_degrees(val))` |
dist | Returns the distance between two points defined by two n-length tuples | `d = dist(xy, (mid_x, mid_y))` or `d = dist(xyz, (mid_x, mid_y, mid_z))` |
rotate_pt | Rotates an x-y point by a certain angle, in degrees | `xy = rotate_pt(xy, 45.0)` or `orig_pt = (1000.0, 1000.0); xy = rotate_pt(xy, 45.0, orig_pt)` |
math::ln | Returns the natural logarithm of the number | `z = math::ln(z)` |
math::log | Returns the logarithm of the number with respect to an arbitrary base | `z = math::log(z, 10)` |
math::log2 | Returns the base 2 logarithm of the number | `z = math::log2(z)` |
math::log10 | Returns the base 10 logarithm of the number | `z = math::log10(z)` |
math::exp | Returns e^(number), (the exponential function) | `z = math::exp(z)` |
math::pow | Raises a number to the power of the other number | `z = math::pow(z, 2.0)` |
math::sqrt | Returns the square root of a number. Returns NaN for a negative number | `z = math::sqrt(z, 2.0)` |
math::cos | Computes the cosine of a number (in radians) | `z = math::cos(to_radians(z))` |
math::sin | Computes the sine of a number (in radians) | `z = math::sin(to_radians(z))` |
math::tan | Computes the tangent of a number (in radians) | `z = math::tan(to_radians(z))` |
math::acos | Computes the arccosine of a number. The return value is in radians in the range [0, pi] or NaN if the number is outside the range [-1, 1] | `z = math::acos(z)` |
math::asin | Computes the arcsine of a number. The return value is in radians in the range [0, pi] or NaN if the number is outside the range [-1, 1] | `z = math::asin(z)` |
math::atan | Computes the arctangent of a number. The return value is in radians in the range [0, pi] or NaN if the number is outside the range [-1, 1] | `z = math::atan(z)` |
rand | Returns a random value between 0 and 1, with an optional seed value | `rgb = (int(255.0 * rand()), int(255.0 * rand()), int(255.0 * rand()))` |
helmert_transformation | Performs a Helmert transformation on a point using a 7-parameter transform | `xyz = helmert_transformation(xyz, −446.448, 125.157, −542.06, 20.4894, −0.1502, −0.247, −0.8421 )` |
The hyperbolic trigonometric functions are also available for use in expression building, as is math::atan2
and the mathematical constants pi
and e
.
You may use if
operations within statements to implement a conditional modification of point properties. For example, the following expression demonstrates how you could modify a point's RGB colour based on its classification, assign ground points (class 2) in the output file a green colour:
rgb = if(class==2, (0,255,0), rgb)
To colour all points within 50 m of the tile mid-point red and all other points blue:
rgb = if(dist(xy, (mid_x, mid_y))<50.0, (255,0,0), (0,0,255))
if
operations may also be nested to create more complex compound conditional point modification. For example, in the following statement, we assign first-return points red (255,0,0) and last-return points green (0,255,0) colours and white (255,255,255) to all other points (intermediate-returns and only-returns):
rgb = if(is_first, (255,0,0), if(is_last, (0,255,0), (255,255,255)))
Here we use an if
expression to re-classify points above an elevation of 1000.0 as high noise (class 18):
class = if(z>1000.0, 18, class)
Expressions may be strung together within statements using semicolons (;), with each expression being evaluated individually. When this is the case, at least one of the expressions must assign a value to one of the variant point properties (see table above). The following statement demonstrates multi-expression statements, in this case to swap the x and y coordinates in a LiDAR file:
new_var = x; x = y; y = new_var
The rand
function, used with the seeding option, can be useful when assigning colours to points based on common point properties. For example, to assign a point a random RGB colour based on its point_source_id
(Note, for many point clouds, this operation will assign each flightline a unique colour; if flightline information is not stored in the file's point_source_id
attribute, one could use the recover_flightline_info tool to calculate this data.):
rgb=(int(255 * rand(point_source_id)), int(255 * rand(point_source_id+1)), int(255 * rand(point_source_id+2)))
This expression-based approach to modifying point properties provides a great deal of flexibility and power to the processing of LiDAR point cloud data sets.
filter_lidar, sort_lidar, lidar_eigenvalue_features