{"id":1448,"date":"2026-10-23T12:32:36","date_gmt":"2026-10-23T12:32:36","guid":{"rendered":"https:\/\/helpwithmygisassignment.com\/blog\/?p=1448"},"modified":"2026-10-23T12:41:24","modified_gmt":"2026-10-23T12:41:24","slug":"automated-metadata-generation-python-iso-standards","status":"publish","type":"post","link":"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/","title":{"rendered":"Programmatic LiDAR Point Cloud Classification in Python Using laspy and Scikit-Learn"},"content":{"rendered":"<div>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_89 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-1'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#Programmatic_LiDAR_Point_Cloud_Classification_in_Python_Using_laspy_and_Scikit-Learn\" >Programmatic LiDAR Point Cloud Classification in Python Using laspy and Scikit-Learn<\/a><ul class='ez-toc-list-level-2' ><li class='ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#Introduction_sing_Automated_Metadata_Generation_Python_ISO_Standards_in_Practice\" >Introduction: sing Automated Metadata Generation Python ISO Standards in Practice<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#1_Environment_Setup_and_Prerequisites\" >1. Environment Setup and Prerequisites<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#Required_libraries\" >Required libraries<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#2_Understanding_LAS_and_LAZ_Point-Cloud_Data\" >2. Understanding LAS and LAZ Point-Cloud Data<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#Standard_classification_codes\" >Standard classification codes<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#3_Step_One_Read_LAS_and_LAZ_Files_in_Python\" >3. Step One: Read LAS and LAZ Files in Python<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#4_Step_Two_Noise_Reduction_and_Voxel_Downsampling_with_Open3D\" >4. Step Two: Noise Reduction and Voxel Downsampling with Open3D<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#5_Step_Three_Geometric_Feature_Engineering\" >5. Step Three: Geometric Feature Engineering<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#Understanding_local_covariance_features\" >Understanding local covariance features<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#Calculate_features_using_a_KD-tree\" >Calculate features using a KD-tree<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#6_Step_Four_Train_a_Random_Forest_Classifier\" >6. Step Four: Train a Random Forest Classifier<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#Prepare_the_training_data\" >Prepare the training data<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-14\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#Fit_the_model_and_evaluate_predictions\" >Fit the model and evaluate predictions<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-15\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#7_Step_Five_Classify_the_Target_Point_Cloud\" >7. Step Five: Classify the Target Point Cloud<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-16\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#8_Step_Six_Export_Classified_Points_to_LAS\" >8. Step Six: Export Classified Points to LAS<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-17\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#Extract_ground_returns\" >Extract ground returns<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-18\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#9_Performance_Optimization_for_Large_Point_Clouds\" >9. Performance Optimization for Large Point Clouds<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-19\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#Use_chunked_LASLAZ_reading\" >Use chunked LAS\/LAZ reading<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-20\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#Additional_optimization_techniques\" >Additional optimization techniques<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-21\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#10_Common_Pitfalls_and_Troubleshooting\" >10. Common Pitfalls and Troubleshooting<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-22\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#11_SEO_Interlinking_and_Further_Reading\" >11. SEO Interlinking and Further Reading<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-23\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/#Conclusion\" >Conclusion<\/a><\/li><\/ul><\/li><\/ul><\/nav><\/div>\n<h1><span class=\"ez-toc-section\" id=\"Programmatic_LiDAR_Point_Cloud_Classification_in_Python_Using_laspy_and_Scikit-Learn\"><\/span>Programmatic LiDAR Point Cloud Classification in Python Using laspy and Scikit-Learn<span class=\"ez-toc-section-end\"><\/span><\/h1>\n<h2><span class=\"ez-toc-section\" id=\"Introduction_sing_Automated_Metadata_Generation_Python_ISO_Standards_in_Practice\"><\/span>Introduction: sing Automated Metadata Generation Python ISO Standards in Practice<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>When implementing\u00a0 <strong>Automated Metadata Generation Python ISO Standard<\/strong>s pipelines, geospatial professionals often struggle with manual XML template formatting. Light Detection and Ranging (LiDAR) technology captures detailed three-dimensional representations of the Earth&#8217;s surface by recording millions of laser returns. These measurements support digital terrain modeling, forest inventory, building extraction, power-line corridor inspection, and infrastructure mapping.<\/p>\n<p>Traditionally, GIS professionals have relied on desktop applications for point-cloud filtering, ground classification, and visualization. However, programmatic workflows using Python make it possible to automate these tasks, integrate classification into spatial machine-learning pipelines, and process multiple datasets without manually operating proprietary software.<\/p>\n<p>The challenge is that raw <code>.las<\/code> and <code>.laz<\/code> files contain large collections of unorganized three-dimensional points. Each point may include coordinates, intensity, return information, classification flags, and other attributes. Dense point clouds can consume several gigabytes of memory, while meaningful classification requires understanding each point&#8217;s local geometry and context.<\/p>\n<p>This guide demonstrates how to build a practical <strong>LiDAR point cloud classification Python laspy scikit-learn<\/strong> pipeline. We will read point-cloud data, explore noise reduction and voxel downsampling with Open3D, engineer local geometric features, train a Random Forest classifier, evaluate its predictions, and export the results into a standards-compliant LAS file.<\/p>\n<p>The example targets four classes: ground, buildings, vegetation canopy, and power lines.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"1_Environment_Setup_and_Prerequisites\"><\/span>1. Environment Setup and Prerequisites<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Python provides a flexible collection of open-source libraries for LiDAR processing and machine learning.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Required_libraries\"><\/span>Required libraries<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<table>\n<tbody>\n<tr>\n<th>Library<\/th>\n<th>Purpose<\/th>\n<\/tr>\n<tr>\n<td><code>laspy<\/code><\/td>\n<td>Read, modify, and write LAS\/LAZ point-cloud files<\/td>\n<\/tr>\n<tr>\n<td><code>numpy<\/code><\/td>\n<td>Efficient numerical operations on coordinates and features<\/td>\n<\/tr>\n<tr>\n<td><code>scikit-learn<\/code><\/td>\n<td>Train classifiers and evaluate predictions<\/td>\n<\/tr>\n<tr>\n<td><code>open3d<\/code><\/td>\n<td>Point-cloud downsampling, filtering, and visualization<\/td>\n<\/tr>\n<tr>\n<td><code>matplotlib<\/code><\/td>\n<td>Plot confusion matrices and diagnostic charts<\/td>\n<\/tr>\n<tr>\n<td><code>scipy<\/code><\/td>\n<td>Efficient nearest-neighbor searches using KD-trees<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Create a virtual environment and install the dependencies:<\/p>\n<pre><code>python -m venv lidar-env<\/code><\/pre>\n<p>Activate it on Windows:<\/p>\n<pre><code>lidar-env\\Scripts\\activate<\/code><\/pre>\n<p>On Linux or macOS:<\/p>\n<pre><code>source lidar-env\/bin\/activate<\/code><\/pre>\n<p>Install the libraries, including an optional LAZ compression backend:<\/p>\n<pre><code>pip install \"laspy[lazrs]\" numpy scikit-learn open3d matplotlib scipy<\/code><\/pre>\n<p><code>laspy<\/code> supports LAS directly and uses optional backends such as <code>lazrs<\/code> to read and write compressed LAZ files. Review the <a href=\"https:\/\/laspy.readthedocs.io\/en\/latest\/installation.html\" target=\"_blank\" rel=\"noopener\">laspy installation documentation<\/a> if your dataset uses compression or requires a different backend.<\/p>\n<p>You should also have a labeled training dataset containing representative examples of the classes you want to identify. Supervised machine learning cannot learn ground, buildings, vegetation, or power lines from geometry alone without reference labels.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"2_Understanding_LAS_and_LAZ_Point-Cloud_Data\"><\/span>2. Understanding LAS and LAZ Point-Cloud Data<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>LAS is a standardized format for exchanging LiDAR point-cloud data. LAZ stores the same type of information using lossless compression.<\/p>\n<p>A point record may contain:<\/p>\n<ul>\n<li>X, Y, and Z coordinates.<\/li>\n<li>Laser-return intensity.<\/li>\n<li>Return number and number of returns.<\/li>\n<li>Classification and classification flags.<\/li>\n<li>GPS time, scan angle, and point source.<\/li>\n<li>RGB or near-infrared attributes, when available.<\/li>\n<\/ul>\n<p>The LAS header stores metadata such as the point format, coordinate scales, offsets, bounds, and potentially coordinate reference system information.<\/p>\n<p>One important distinction in <code>laspy<\/code> is that uppercase dimensions such as <code>las.X<\/code>, <code>las.Y<\/code>, and <code>las.Z<\/code> represent the underlying integer coordinates. The lowercase attributes <code>las.x<\/code>, <code>las.y<\/code>, and <code>las.z<\/code> expose coordinates with the header&#8217;s scale and offset applied.<\/p>\n<p>For geometric calculations, use the scaled coordinates.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Standard_classification_codes\"><\/span>Standard classification codes<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>The example uses these ASPRS-compatible class codes:<\/p>\n<table>\n<tbody>\n<tr>\n<th>Target class<\/th>\n<th>LAS classification code<\/th>\n<\/tr>\n<tr>\n<td>Ground<\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td>Canopy<\/td>\n<td>5 \u2014 high vegetation<\/td>\n<\/tr>\n<tr>\n<td>Building<\/td>\n<td>6<\/td>\n<\/tr>\n<tr>\n<td>Power line<\/td>\n<td>14 \u2014 wire conductor<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>These codes follow the <a href=\"https:\/\/www.asprs.org\/wp-content\/uploads\/2019\/03\/LAS_1_4_r14.pdf\" target=\"_blank\" rel=\"noopener\">ASPRS LAS 1.4 specification<\/a>.<\/p>\n<p>The canopy class is represented by high vegetation in this simplified workflow, while power lines are represented by wire-conductor points. These semantic groupings are not interchangeable with arbitrary machine-learning label numbers. Always document your mapping when training and exporting classifications.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"3_Step_One_Read_LAS_and_LAZ_Files_in_Python\"><\/span>3. Step One: Read LAS and LAZ Files in Python<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Begin by loading a labeled reference point cloud and a separate target point cloud. In a production workflow, the reference file should contain reliably labeled points from one or more representative scenes.<\/p>\n<pre><code>import laspy\r\nimport numpy as np\r\n\r\ntrain_las = laspy.read(\"labeled_training.las\")\r\ntarget_las = laspy.read(\"target.las\")\r\n\r\ndef extract_coordinates(las):\r\n    return np.column_stack((\r\n        las.x,\r\n        las.y,\r\n        las.z\r\n    )).astype(np.float32)\r\n\r\ntrain_xyz = extract_coordinates(train_las)\r\ntarget_xyz = extract_coordinates(target_las)\r\n\r\ntrain_intensity = np.asarray(\r\n    train_las.intensity,\r\n    dtype=np.float32\r\n)\r\n\r\ntarget_intensity = np.asarray(\r\n    target_las.intensity,\r\n    dtype=np.float32\r\n)\r\n\r\ntrain_labels = np.asarray(\r\n    train_las.classification,\r\n    dtype=np.uint8\r\n)\r\n\r\nprint(\"Training points:\", len(train_xyz))\r\nprint(\"Target points:\", len(target_xyz))\r\nprint(\"LAS version:\", target_las.header.version)\r\nprint(\"Point format:\", target_las.header.point_format.id)\r\nprint(\"Available dimensions:\",\r\n      list(target_las.point_format.dimension_names))<\/code><\/pre>\n<p>The two input files serve different purposes. The labeled dataset supplies examples for learning the relationship between point-cloud features and object classes. The target dataset is the scene to classify.<\/p>\n<p><strong>Important:<\/strong> This example loads both files into memory. For large datasets, use the chunked-processing strategy described later rather than assuming every tile will fit into RAM.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"4_Step_Two_Noise_Reduction_and_Voxel_Downsampling_with_Open3D\"><\/span>4. Step Two: Noise Reduction and Voxel Downsampling with Open3D<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Raw LiDAR datasets can contain isolated returns, measurement noise, and uneven sampling density. Open3D provides statistical outlier removal and voxel-grid downsampling to help inspect and simplify point clouds.<\/p>\n<p>Statistical outlier removal identifies points whose average neighbor distances are unusually large. Voxel downsampling groups nearby points into three-dimensional grid cells and represents each occupied cell with a representative point.<\/p>\n<pre><code>import open3d as o3d\r\n\r\ndef downsample_for_inspection(xyz, voxel_size=0.25):\r\n    cloud = o3d.geometry.PointCloud()\r\n    cloud.points = o3d.utility.Vector3dVector(\r\n        xyz.astype(np.float64)\r\n    )\r\n\r\n    # Remove statistical outliers.\r\n    filtered, _ = cloud.remove_statistical_outlier(\r\n        nb_neighbors=20,\r\n        std_ratio=2.0\r\n    )\r\n\r\n    # Reduce point density with a voxel grid.\r\n    downsampled = filtered.voxel_down_sample(\r\n        voxel_size=voxel_size\r\n    )\r\n\r\n    return downsampled\r\n\r\npreview_cloud = downsample_for_inspection(\r\n    target_xyz,\r\n    voxel_size=0.25\r\n)\r\n\r\nprint(\"Original points:\", len(target_xyz))\r\nprint(\"Preview points:\", len(preview_cloud.points))\r\n\r\no3d.visualization.draw_geometries([preview_cloud])<\/code><\/pre>\n<p>Here, <code>voxel_size=0.25<\/code> uses a 25-centimeter voxel when the coordinates are expressed in meters. Select the voxel size according to point density, object dimensions, and the level of detail required.<\/p>\n<p>A critical limitation applies: voxel downsampling changes the point representation, and statistical filtering may remove legitimate sparse returns. Thin power lines are particularly vulnerable to aggressive filtering.<\/p>\n<p>Therefore, <strong>use this reduced cloud for inspection and exploratory analysis, not as a replacement for the original point records in the final classified output<\/strong>. The next steps calculate features from the original point geometry so every target point can receive a prediction.<\/p>\n<p>For more detail, see the <a href=\"https:\/\/www.open3d.org\/docs\/latest\/python_api\/open3d.geometry.PointCloud.html\" target=\"_blank\" rel=\"noopener\">Open3D point-cloud API documentation<\/a>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-1554\" src=\"https:\/\/helpwithmygisassignment.com\/blog\/wp-content\/uploads\/2026\/10\/Gemini_Generated_Image_it3jjwit3jjwit3j-1024x572.jpg\" alt=\"3D visualization comparing noisy raw LiDAR point data with a cleaned, voxel-downsampled point cloud in Open3D.\" width=\"1024\" height=\"572\" srcset=\"https:\/\/helpwithmygisassignment.com\/blog\/wp-content\/uploads\/2026\/10\/Gemini_Generated_Image_it3jjwit3jjwit3j-1024x572.jpg 1024w, https:\/\/helpwithmygisassignment.com\/blog\/wp-content\/uploads\/2026\/10\/Gemini_Generated_Image_it3jjwit3jjwit3j-300x167.jpg 300w, https:\/\/helpwithmygisassignment.com\/blog\/wp-content\/uploads\/2026\/10\/Gemini_Generated_Image_it3jjwit3jjwit3j-768x429.jpg 768w, https:\/\/helpwithmygisassignment.com\/blog\/wp-content\/uploads\/2026\/10\/Gemini_Generated_Image_it3jjwit3jjwit3j.jpg 1376w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<h2><span class=\"ez-toc-section\" id=\"5_Step_Three_Geometric_Feature_Engineering\"><\/span>5. Step Three: Geometric Feature Engineering<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>The success of machine-learning point-cloud classification depends heavily on feature quality. Raw coordinates alone rarely provide enough information to distinguish all object types reliably.<\/p>\n<p>A flat roof, a ground surface, and a patch of vegetation can occupy similar elevations. Local geometric features describe how points are distributed around each return, helping the classifier distinguish surface shapes.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Understanding_local_covariance_features\"><\/span>Understanding local covariance features<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>For each point, identify its nearest neighbors and compute a local covariance matrix. Its eigenvalues describe the dominant directions of spatial variation.<\/p>\n<p>Let the ordered eigenvalues satisfy (\\lambda_1 \\geq \\lambda_2 \\geq \\lambda_3 \\geq 0).<\/p>\n<p>Three commonly used features are:<\/p>\n<ul>\n<li><strong>Linearity:<\/strong> ((\\lambda_1-\\lambda_2)\/\\lambda_1). High values indicate a dominant linear structure.<\/li>\n<li><strong>Planarity:<\/strong> ((\\lambda_2-\\lambda_3)\/\\lambda_1). High values indicate a locally planar surface.<\/li>\n<li><strong>Sphericity:<\/strong> (\\lambda_3\/\\lambda_1). Higher values indicate a more isotropic, volumetric neighborhood.<\/li>\n<\/ul>\n<p>Verticality is derived from the local surface normal, which corresponds to the eigenvector associated with the smallest eigenvalue. If (n_z) is the vertical component of that normal, define verticality as (1-|n_z|). This makes vertical surfaces score higher than horizontal surfaces.<\/p>\n<p>We also calculate local roughness and relative height above a low neighborhood elevation percentile. These features help distinguish ground returns from elevated surfaces.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Calculate_features_using_a_KD-tree\"><\/span>Calculate features using a KD-tree<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>The following implementation uses <code>scipy.spatial.cKDTree<\/code> for efficient nearest-neighbor queries and NumPy for covariance calculations.<\/p>\n<pre><code>from scipy.spatial import cKDTree\r\n\r\ndef compute_features(xyz, intensity, k=24):\r\n    xyz = np.asarray(xyz, dtype=np.float32)\r\n    intensity = np.asarray(intensity, dtype=np.float32)\r\n\r\n    if len(xyz) &lt; 3:\r\n        raise ValueError(\r\n            \"At least three points are required.\"\r\n        )\r\n\r\n    k_eff = min(k + 1, len(xyz))\r\n\r\n    tree = cKDTree(xyz)\r\n    _, indices = tree.query(\r\n        xyz,\r\n        k=k_eff,\r\n        workers=-1\r\n    )\r\n\r\n    # Exclude the nearest result, normally the point itself.\r\n    neighbors = xyz[indices[:, 1:]]\r\n\r\n    # Local covariance matrices.\r\n    centered = neighbors - neighbors.mean(\r\n        axis=1,\r\n        keepdims=True\r\n    )\r\n\r\n    covariance = np.einsum(\r\n        \"nki,nkj-&gt;nij\",\r\n        centered,\r\n        centered\r\n    ) \/ neighbors.shape[1]\r\n\r\n    eigenvalues, eigenvectors = np.linalg.eigh(\r\n        covariance\r\n    )\r\n\r\n    l3 = np.maximum(eigenvalues[:, 0], 0)\r\n    l2 = np.maximum(eigenvalues[:, 1], 0)\r\n    l1 = np.maximum(eigenvalues[:, 2], 1e-12)\r\n\r\n    linearity = (l1 - l2) \/ l1\r\n    planarity = (l2 - l3) \/ l1\r\n    sphericity = l3 \/ l1\r\n\r\n    # Normal is the eigenvector of the smallest eigenvalue.\r\n    normal_z = np.abs(eigenvectors[:, 2, 0])\r\n    verticality = 1.0 - normal_z\r\n\r\n    neighbor_z = neighbors[:, :, 2]\r\n    relative_height = (\r\n        xyz[:, 2]\r\n        - np.quantile(neighbor_z, 0.10, axis=1)\r\n    )\r\n\r\n    roughness = np.sqrt(l3)\r\n\r\n    # Approximate normalization for typical 16-bit intensity.\r\n    intensity_norm = intensity \/ 65535.0\r\n\r\n    return np.column_stack((\r\n        intensity_norm,\r\n        relative_height,\r\n        linearity,\r\n        planarity,\r\n        sphericity,\r\n        verticality,\r\n        roughness\r\n    )).astype(np.float32)<\/code><\/pre>\n<p>Each point now has seven features: normalized intensity, relative height, linearity, planarity, sphericity, verticality, and roughness.<\/p>\n<p>The neighborhood parameter matters. A small neighborhood captures fine structures, while a larger neighborhood captures broader surface patterns. Test several values on representative data.<\/p>\n<p>This implementation is suitable for manageable point-cloud tiles. Its neighbor-index arrays and covariance calculations can consume substantial memory when millions of points are processed simultaneously.<\/p>\n<p>Also note that local features depend on point density and neighborhood composition. For robust transfer between datasets, keep feature definitions consistent and evaluate on geographically independent data.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"6_Step_Four_Train_a_Random_Forest_Classifier\"><\/span>6. Step Four: Train a Random Forest Classifier<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>A Random Forest combines many decision trees to predict a class from a feature vector. It works well as a baseline for LiDAR classification because it can capture nonlinear feature relationships and does not require feature standardization.<\/p>\n<p>We will train the model to distinguish ground, canopy, buildings, and power lines.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Prepare_the_training_data\"><\/span>Prepare the training data<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<pre><code>from sklearn.ensemble import RandomForestClassifier\r\nfrom sklearn.model_selection import train_test_split\r\nfrom sklearn.metrics import (\r\n    classification_report,\r\n    ConfusionMatrixDisplay\r\n)\r\nimport matplotlib.pyplot as plt\r\n\r\nCLASS_CODES = {\r\n    \"ground\": 2,\r\n    \"canopy\": 5,\r\n    \"building\": 6,\r\n    \"power_line\": 14\r\n}\r\n\r\nclass_codes = list(CLASS_CODES.values())\r\nclass_names = list(CLASS_CODES.keys())\r\n\r\nX_all = compute_features(\r\n    train_xyz,\r\n    train_intensity,\r\n    k=24\r\n)\r\n\r\nvalid = np.isin(train_labels, class_codes)\r\n\r\nX = X_all[valid]\r\ny = train_labels[valid]\r\n\r\nif len(y) == 0:\r\n    raise ValueError(\r\n        \"No training points match the requested class codes.\"\r\n    )\r\n\r\nprint(\"Training samples:\", len(y))\r\nprint(\"Classes represented:\", np.unique(y))<\/code><\/pre>\n<p>The reference file must contain actual labels for the requested classes. A model cannot learn four classes if some are missing from the training samples.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Fit_the_model_and_evaluate_predictions\"><\/span>Fit the model and evaluate predictions<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<pre><code>X_train, X_test, y_train, y_test = train_test_split(\r\n    X,\r\n    y,\r\n    test_size=0.20,\r\n    random_state=42,\r\n    stratify=y\r\n)\r\n\r\nmodel = RandomForestClassifier(\r\n    n_estimators=300,\r\n    min_samples_leaf=2,\r\n    class_weight=\"balanced_subsample\",\r\n    n_jobs=-1,\r\n    random_state=42\r\n)\r\n\r\nmodel.fit(X_train, y_train)\r\n\r\ny_pred = model.predict(X_test)\r\n\r\nprint(classification_report(\r\n    y_test,\r\n    y_pred,\r\n    labels=class_codes,\r\n    target_names=class_names,\r\n    zero_division=0\r\n))\r\n\r\nConfusionMatrixDisplay.from_predictions(\r\n    y_test,\r\n    y_pred,\r\n    labels=class_codes,\r\n    display_labels=class_names,\r\n    cmap=\"Blues\",\r\n    xticks_rotation=45\r\n)\r\n\r\nplt.title(\"LiDAR Point Classification: Random Forest\")\r\nplt.tight_layout()\r\nplt.savefig(\"lidar_confusion_matrix.png\", dpi=200)\r\nplt.show()<\/code><\/pre>\n<p>The classification report gives precision, recall, F1-score, and sample support for each class. The confusion matrix reveals which categories are being confused. For example, a model may mistake vegetation for ground or classify building edges as power lines.<\/p>\n<p>The random train-test split above is intended as a basic demonstration. Neighboring LiDAR points are spatially correlated, so randomly splitting points from the same scene can overestimate real-world accuracy. For a credible assessment, hold out an entire spatial block, survey area, or separate labeled tile whenever possible.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"7_Step_Five_Classify_the_Target_Point_Cloud\"><\/span>7. Step Five: Classify the Target Point Cloud<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-1555\" src=\"https:\/\/helpwithmygisassignment.com\/blog\/wp-content\/uploads\/2026\/10\/Gemini_Generated_Image_tpz5xwtpz5xwtpz5-1024x572.jpg\" alt=\"Classified LiDAR point cloud showing color-coded ground, buildings, tree canopy, and power line classes.\" width=\"1024\" height=\"572\" srcset=\"https:\/\/helpwithmygisassignment.com\/blog\/wp-content\/uploads\/2026\/10\/Gemini_Generated_Image_tpz5xwtpz5xwtpz5-1024x572.jpg 1024w, https:\/\/helpwithmygisassignment.com\/blog\/wp-content\/uploads\/2026\/10\/Gemini_Generated_Image_tpz5xwtpz5xwtpz5-300x167.jpg 300w, https:\/\/helpwithmygisassignment.com\/blog\/wp-content\/uploads\/2026\/10\/Gemini_Generated_Image_tpz5xwtpz5xwtpz5-768x429.jpg 768w, https:\/\/helpwithmygisassignment.com\/blog\/wp-content\/uploads\/2026\/10\/Gemini_Generated_Image_tpz5xwtpz5xwtpz5.jpg 1376w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p>After evaluating the model, calculate the same features for the target file and generate predictions.<\/p>\n<pre><code>target_features = compute_features(\r\n    target_xyz,\r\n    target_intensity,\r\n    k=24\r\n)\r\n\r\ntarget_predictions = model.predict(\r\n    target_features\r\n)\r\n\r\nprint(\"Predicted points:\", len(target_predictions))\r\nprint(\r\n    \"Predicted LAS classes:\",\r\n    np.unique(target_predictions)\r\n)<\/code><\/pre>\n<p>The model uses the same feature order and parameters as during training. Changing the neighborhood size, intensity transformation, or feature definitions between training and prediction can undermine the results.<\/p>\n<p>Predictions may still be imperfect. Thin wires, tree branches, building edges, and low-density areas often require careful feature tuning or additional return attributes.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"8_Step_Six_Export_Classified_Points_to_LAS\"><\/span>8. Step Six: Export Classified Points to LAS<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>A reliable export should preserve the original file&#8217;s coordinate information and metadata wherever possible.<\/p>\n<p>Rather than constructing a new file from XYZ arrays, update the classification dimension on the loaded target object. This preserves its original point records and existing header information, including coordinate scales, offsets, point format, and available CRS metadata.<\/p>\n<pre><code># Assign predicted ASPRS-compatible class codes.\r\ntarget_las.classification = (\r\n    target_predictions.astype(np.uint8)\r\n)\r\n\r\n# Save as an uncompressed LAS file.\r\ntarget_las.write(\"classified_target.las\")\r\n\r\nprint(\"Saved classified_target.las\")<\/code><\/pre>\n<p>To write compressed LAZ output instead, change the filename:<\/p>\n<pre><code>target_las.write(\"classified_target.laz\")<\/code><\/pre>\n<p>The installed LAZ backend must support the data being written. With the optional <code>lazrs<\/code> dependency installed, typical LAS\/LAZ workflows are supported, subject to backend limitations.<\/p>\n<p>Because the example retains all original point records and coordinates, it does not need to reconstruct the LAS header from scratch. This is safer than creating a new default header, which may lose the source coordinate scales, offsets, CRS information, or other metadata.<\/p>\n<p>If you create an entirely new point cloud from arrays, you must explicitly choose a compatible LAS version and point format, set appropriate coordinate scales and offsets, preserve or add CRS metadata, and populate the supported point dimensions correctly.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Extract_ground_returns\"><\/span>Extract ground returns<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>The classified file can now be used to isolate ground points for digital terrain modeling or additional surface analysis.<\/p>\n<pre><code>ground_mask = (\r\n    target_las.classification\r\n    == CLASS_CODES[\"ground\"]\r\n)\r\n\r\nground_points = target_las.points[ground_mask]\r\n\r\nground_las = laspy.LasData(\r\n    target_las.header.copy()\r\n)\r\n\r\nground_las.points = ground_points\r\nground_las.write(\"ground_returns.las\")<\/code><\/pre>\n<p>The output contains only the points classified as ground, with the source header copied as a starting point. Validate the resulting file in a LAS-aware viewer or with <code>laspy<\/code> before downstream use.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"9_Performance_Optimization_for_Large_Point_Clouds\"><\/span>9. Performance Optimization for Large Point Clouds<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Processing a multi-gigabyte LiDAR tile requires a different strategy from processing a small sample. Loading all coordinates, neighbors, covariance matrices, and features simultaneously can exhaust available memory.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Use_chunked_LASLAZ_reading\"><\/span>Use chunked LAS\/LAZ reading<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><code>laspy.open()<\/code> supports reading point records in chunks. This is useful for inspection, filtering, conversion, and larger workflows.<\/p>\n<pre><code>import laspy\r\n\r\nwith laspy.open(\"large_tile.laz\") as reader:\r\n    print(\"Total points:\", reader.header.point_count)\r\n\r\n    for points in reader.chunk_iterator(500_000):\r\n        xyz = np.column_stack((\r\n            points.x,\r\n            points.y,\r\n            points.z\r\n        ))\r\n\r\n        intensity = points.intensity\r\n\r\n        # Process this chunk or write its records\r\n        # to an appropriate intermediate dataset.<\/code><\/pre>\n<p>This example shows memory-conscious reading, not a complete neighborhood-aware classifier. A <code>chunk_iterator<\/code> divides the file&#8217;s record sequence, which is not guaranteed to correspond to spatially compact blocks. Points close to the boundary of two chunks may therefore be neighbors even when they appear in different batches.<\/p>\n<p>For geometric feature extraction, use <strong>spatial tiles with overlapping buffer zones<\/strong>. Compute features using the core tile plus its surrounding halo, predict classes for the core points, and write each core exactly once. Buffer width and neighborhood size must be chosen to capture sufficient spatial context.<\/p>\n<p>A scalable production pipeline can follow this pattern:<\/p>\n<ol start=\"1\">\n<li>Partition the point cloud into spatial tiles.<\/li>\n<li>Read each tile together with a neighborhood buffer.<\/li>\n<li>Compute local features using the buffered points.<\/li>\n<li>Predict classes for the tile&#8217;s core points only.<\/li>\n<li>Write predictions and retain the original point dimensions.<\/li>\n<li>Merge or index the completed outputs where appropriate.<\/li>\n<\/ol>\n<p>For extremely large datasets, consider spatially indexed formats and tiling tools rather than assuming a single Python array can represent the entire scene.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Additional_optimization_techniques\"><\/span>Additional optimization techniques<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><strong>Reduce feature-array memory.<\/strong> Use <code>float32<\/code> for coordinates and feature matrices where its precision is adequate. Process smaller tiles to limit the size of temporary neighbor arrays.<\/p>\n<p><strong>Tune the neighborhood size.<\/strong> Increasing the number of neighbors increases computational cost and may blur boundaries. Start with a moderate value such as 24, then benchmark alternative settings against labeled validation data.<\/p>\n<p><strong>Avoid indiscriminate denoising.<\/strong> Statistical filters can remove legitimate low-density returns, especially wires and fine vegetation. Preserve original records and evaluate filtering choices by class.<\/p>\n<p><strong>Use multiple informative attributes.<\/strong> Where available, include return number, number of returns, RGB, scan angle, and additional sensor attributes. Normalize and document each feature consistently.<\/p>\n<p><strong>Use class-aware evaluation.<\/strong> Overall accuracy can hide poor power-line detection when power-line returns constitute a tiny fraction of the dataset. Compare per-class precision, recall, and F1-score, and investigate errors using a 3D viewer.<\/p>\n<p><strong>Parallelize carefully.<\/strong> Random Forest training can use multiple CPU cores through <code>n_jobs=-1<\/code>. However, parallel jobs and large feature matrices can increase peak memory consumption. Benchmark the complete workflow rather than optimizing a single isolated operation.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"10_Common_Pitfalls_and_Troubleshooting\"><\/span>10. Common Pitfalls and Troubleshooting<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<table>\n<tbody>\n<tr>\n<th>Problem<\/th>\n<th>Likely cause<\/th>\n<th>Recommended action<\/th>\n<\/tr>\n<tr>\n<td>Unable to open LAZ files<\/td>\n<td>Missing or incompatible compression backend<\/td>\n<td>Install <code>laspy[lazrs]<\/code> and verify the backend<\/td>\n<\/tr>\n<tr>\n<td>Poor ground classification<\/td>\n<td>Weak height or local surface features<\/td>\n<td>Tune neighborhood size and add suitable elevation context<\/td>\n<\/tr>\n<tr>\n<td>Power lines classified as vegetation<\/td>\n<td>Sparse returns and class imbalance<\/td>\n<td>Add linearity and return attributes; increase representative wire samples<\/td>\n<\/tr>\n<tr>\n<td>Excellent test score but poor results on new areas<\/td>\n<td>Spatial leakage between training and test points<\/td>\n<td>Validate on separate spatial blocks or tiles<\/td>\n<\/tr>\n<tr>\n<td>Coordinates appear shifted<\/td>\n<td>Scales, offsets, or CRS interpretation errors<\/td>\n<td>Inspect the LAS header and scaled coordinates<\/td>\n<\/tr>\n<tr>\n<td>Excessive memory usage<\/td>\n<td>Large intermediate arrays and global neighbor searches<\/td>\n<td>Use buffered spatial tiles and smaller feature batches<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>A further limitation is that covariance features describe local geometry, not object identity. A narrow branch, cable, and other linear structure may share similar feature values. Better results may require more representative training data, additional sensor attributes, contextual rules, or a specialized point-cloud segmentation model.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"11_SEO_Interlinking_and_Further_Reading\"><\/span>11. SEO Interlinking and Further Reading<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Build topical relevance by connecting this tutorial to related GIS developer resources on your website.<\/p>\n<p>Suggested internal links:<\/p>\n<ul>\n<li><a href=\"https:\/\/chatgpt.com\/guides\/python-geopandas-automation\/\" target=\"_blank\" rel=\"noopener\">Python GeoPandas automation guide<\/a> \u2014 for integrating classified spatial outputs into automated geoprocessing workflows.<\/li>\n<li><a href=\"https:\/\/chatgpt.com\/guides\/spatial-machine-learning\/\" target=\"_blank\" rel=\"noopener\">Spatial machine learning guide<\/a> \u2014 for validation strategies, feature engineering, and model selection.<\/li>\n<li><a href=\"https:\/\/chatgpt.com\/guides\/point-pattern-analysis\/\" target=\"_blank\" rel=\"noopener\">Point pattern analysis guide<\/a> \u2014 for analyzing the spatial distributions of extracted features and classified point populations.<\/li>\n<\/ul>\n<p>Replace these suggested paths with the live URLs on your website.<\/p>\n<p>Recommended external references:<\/p>\n<ul>\n<li><a href=\"https:\/\/laspy.readthedocs.io\/en\/latest\/\" target=\"_blank\" rel=\"noopener\">laspy documentation<\/a> \u2014 LAS\/LAZ reading, writing, and point-record manipulation.<\/li>\n<li><a href=\"https:\/\/www.open3d.org\/docs\/latest\/\" target=\"_blank\" rel=\"noopener\">Open3D documentation<\/a> \u2014 point-cloud filtering, downsampling, and geometric operations.<\/li>\n<li><a href=\"https:\/\/scikit-learn.org\/stable\/modules\/generated\/sklearn.ensemble.RandomForestClassifier.html\" target=\"_blank\" rel=\"noopener\">Scikit-Learn RandomForestClassifier<\/a> \u2014 model parameters and prediction behavior.<\/li>\n<li><a href=\"https:\/\/www.asprs.org\/wp-content\/uploads\/2019\/03\/LAS_1_4_r14.pdf\" target=\"_blank\" rel=\"noopener\">ASPRS LAS 1.4 specification<\/a> \u2014 standard point-class definitions and LAS record structure.<\/li>\n<\/ul>\n<h2><span class=\"ez-toc-section\" id=\"Conclusion\"><\/span>Conclusion<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Programmatic LiDAR point cloud classification in Python provides a repeatable alternative to manually classifying every point in desktop GIS software. By combining <code>laspy<\/code> for LAS\/LAZ data handling, Open3D for exploratory point-cloud operations, NumPy and KD-trees for geometric feature engineering, and Scikit-Learn for Random Forest classification, spatial analysts can develop automated 3D processing pipelines.<\/p>\n<p>The workflow covered in this guide reads source point clouds, derives local geometric features, trains and evaluates a supervised classifier, predicts ground, canopy, building, and power-line classes, and exports the results while preserving source metadata.<\/p>\n<p>For production deployment, prioritize representative reference labels, spatially independent validation, consistent feature calculations, class-aware evaluation, and memory-conscious spatial tiling. These measures are especially important when working with sparse structures, large urban datasets, or multi-gigabyte point-cloud tiles.<\/p>\n<p><strong>Code repository reference:<\/strong> Organize the workflow into a reusable<a href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python\/\"> Python<\/a> project containing feature extraction, model training, validation, prediction, and LAS export modules. Include a <code>requirements.txt<\/code> file, a README explaining the expected input schemas and ASPRS class mappings, and a small sample dataset or synthetic test case. Store trained models and validation metrics alongside documented parameter settings so that subsequent classification runs remain reproducible.<\/p>\n<\/div>\n<div>\n<div>\n<div>Fix the neighbor-edge caseClarify training and target data<\/div>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Programmatic LiDAR Point Cloud Classification in Python Using laspy and Scikit-Learn Introduction: sing Automated Metadata Generation Python ISO Standards in Practice When implementing\u00a0 Automated Metadata Generation Python ISO Standards pipelines, geospatial professionals often struggle with manual XML template formatting. Light Detection and Ranging (LiDAR) technology captures detailed three-dimensional representations of the Earth&#8217;s surface by recording &#8230; <a title=\"Programmatic LiDAR Point Cloud Classification in Python Using laspy and Scikit-Learn\" class=\"read-more\" href=\"https:\/\/helpwithmygisassignment.com\/blog\/automated-metadata-generation-python-iso-standards\/\" aria-label=\"Read more about Programmatic LiDAR Point Cloud Classification in Python Using laspy and Scikit-Learn\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":1555,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1,4],"tags":[],"class_list":["post-1448","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","category-gis-articles"],"_links":{"self":[{"href":"https:\/\/helpwithmygisassignment.com\/blog\/wp-json\/wp\/v2\/posts\/1448","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/helpwithmygisassignment.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/helpwithmygisassignment.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/helpwithmygisassignment.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/helpwithmygisassignment.com\/blog\/wp-json\/wp\/v2\/comments?post=1448"}],"version-history":[{"count":3,"href":"https:\/\/helpwithmygisassignment.com\/blog\/wp-json\/wp\/v2\/posts\/1448\/revisions"}],"predecessor-version":[{"id":1557,"href":"https:\/\/helpwithmygisassignment.com\/blog\/wp-json\/wp\/v2\/posts\/1448\/revisions\/1557"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/helpwithmygisassignment.com\/blog\/wp-json\/wp\/v2\/media\/1555"}],"wp:attachment":[{"href":"https:\/\/helpwithmygisassignment.com\/blog\/wp-json\/wp\/v2\/media?parent=1448"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/helpwithmygisassignment.com\/blog\/wp-json\/wp\/v2\/categories?post=1448"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/helpwithmygisassignment.com\/blog\/wp-json\/wp\/v2\/tags?post=1448"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}