Skip to contents

parallax wraps point cloud workflows in platform-aware R classes and uses a Rust backend for the operations that are likely to become expensive on real data. The installed demo files are intentionally tiny, but they mirror the same flow you would use for LAS/LAZ data.

Demo files

aerial_path <- system.file("extdata", "aerial-demo.csv", package = "parallax")
terrestrial_path <- system.file("extdata", "terrestrial-demo.csv", package = "parallax")

aerial <- px_read(aerial_path, platform = "aerial")
terrestrial <- px_read(terrestrial_path, platform = "terrestrial")

class(aerial)
#> [1] "px_aerial" "px_cloud"
class(terrestrial)
#> [1] "px_terrestrial" "px_cloud"

Aerial workflow

Start with filtering and ground classification.

filtered <- px_filter_statistical(aerial, k = 6, std_ratio = 1.5)
ground <- px_classify_ground(filtered)

c(
  n_points = nrow(filtered$xyz),
  n_ground = ground$n_ground,
  n_nonground = ground$n_nonground
)
#>    n_points    n_ground n_nonground 
#>          18           8          10

The same cloud can feed tree segmentation and summary metrics.

trees <- px_segment_trees(filtered, min_height = 2.0, crown_threshold = 0.75)
metrics <- px_metrics(filtered)

trees$n_segments
#> [1] 3
metrics
#> # A tibble: 1 × 18
#>   n_points x_min x_max y_min y_max z_min z_max x_mean y_mean z_mean  z_sd  z_q05
#>      <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>  <dbl>  <dbl>  <dbl> <dbl>  <dbl>
#> 1       18     0     3     0     2  0.02   5.1   1.61   1.02   2.16  2.04 0.0285
#> # ℹ 6 more variables: z_q25 <dbl>, z_q50 <dbl>, z_q75 <dbl>, z_q95 <dbl>,
#> #   point_density <dbl>, intensity_mean <dbl>

If you want gridded outputs, assign a simple ground/non-ground classification and build canopy products.

filtered$classification <- ifelse(ground$ground_mask, 2L, 1L)

chm <- px_canopy_height_model(filtered, resolution = 1.0)
density <- px_density_map(filtered, resolution = 1.0)

dim(chm$chm)
#> [1] 3 4
dim(density$density)
#> [1] 3 4

Terrestrial workflow

Terrestrial data often leads with local geometry. Here the small demo scan has a dominant plane and enough points for local normal estimation.

scan_with_normals <- px_estimate_normals(terrestrial, k_neighbors = 4L)
planes <- px_detect_planes(
  scan_with_normals,
  distance_threshold = 0.08,
  min_points = 6L,
  num_iterations = 100L
)

length(planes)
#> [1] 1
if (length(planes) > 0) planes[[1]]$equation
#> [1]  0.010000458 -0.005000229  0.999937492 -0.008333354

Where to go next