{"id":37,"date":"2023-02-10T00:54:04","date_gmt":"2023-02-10T00:54:04","guid":{"rendered":"https:\/\/blogs.oregonstate.edu\/schaefkr\/?p=37"},"modified":"2023-02-10T00:54:04","modified_gmt":"2023-02-10T00:54:04","slug":"the-best-python-libraries-for-working-with-lidar-data","status":"publish","type":"post","link":"https:\/\/blogs.oregonstate.edu\/schaefkr\/2023\/02\/10\/the-best-python-libraries-for-working-with-lidar-data\/","title":{"rendered":"The Best Python Libraries for Working with Lidar Data"},"content":{"rendered":"\n<p>Obsessed with point clouds and Python? Me too! <\/p>\n\n\n\n<p>Recently I discovered that the USGS has tons of free lidar survey data available on <a href=\"https:\/\/apps.nationalmap.gov\/viewer\/\">The National Map<\/a>. Free lidar data is great, but what&#8217;s the best way to extract the data in these files to suit your needs? There&#8217;s open source software available such as <a href=\"https:\/\/www.cloudcompare.org\/\">CloudCompare<\/a> to help you visualize your point cloud data, but if you&#8217;re a coder and you&#8217;re not interested in visualization then it can be tedious and frustrating to extract the right data or file type. Thankfully there are plenty of libraries out there for process lidar data. Check out below to see my top 3 picks for Python libraries.<\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"673\" data-id=\"38\" src=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/6543\/files\/2023\/02\/ViewCapture20221222_170534_bw-1024x673.png\" alt=\"\" class=\"wp-image-38\" srcset=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/6543\/files\/2023\/02\/ViewCapture20221222_170534_bw-1024x673.png 1024w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/6543\/files\/2023\/02\/ViewCapture20221222_170534_bw-300x197.png 300w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/6543\/files\/2023\/02\/ViewCapture20221222_170534_bw-768x505.png 768w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/6543\/files\/2023\/02\/ViewCapture20221222_170534_bw.png 1318w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">A quick experiment visualizing USGS lidar data of SLU Seattle which I made with Laspy and Rhino<\/figcaption><\/figure>\n<\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Laspy<\/h2>\n\n\n\n<p><a href=\"https:\/\/pypi.org\/project\/laspy\/\">Laspy<\/a> is my favorite Python library to use for working with <em>.las<\/em> and <em>.laz<\/em> files. If you&#8217;re a fan of <a href=\"https:\/\/numpy.org\/\">Numpy<\/a> then you&#8217;ll be off and running in no time since Laspy works directly with Numpy arrays. I like Laspy because it&#8217;s a simple, straight forward library which allows you to do all of your basic reading and writing operations with <a href=\"https:\/\/laspy.readthedocs.io\/en\/latest\/basic.html\">great examples<\/a> for support. I also had no installation problems with Laspy on Mac or Windows whereas with <a href=\"https:\/\/liblas.org\/\">libLAS<\/a> I dealt with a lot of frustration trying to get it working on Windows. <\/p>\n\n\n\n<p>Here&#8217;s an example of some code I wrote to read a .las file from the USGS TNM then write each point to a <a href=\"https:\/\/www.rhino3d.com\/mcneel\/about\/\">Rhino<\/a> .3dm point cloud organized by classification code. Note I did use a constants file to specify which classification codes I wanted to use.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import laspy\nimport numpy as np\nimport rhino3dm\nimport constants\n\nif __name__ == '__main__':\n    # Get Data from Las File with Laspy\n    las_filepath = r'filename.las'\n    with laspy.open(las_filepath) as f:\n        for points in f.chunk_iterator(1000000000):\n            x, y, z, classification_codes = points.x.copy(), points.y.copy(), points.z.copy(), points.classification.copy()\n    pts = zip(x.tolist(), y.tolist(), z.tolist(), classification_codes.tolist())\n    classification_codes = np.unique(np.array(classification_codes.tolist()))\n    \n    # Create Rhino 3dm file\n    model = rhino3dm.File3dm()\n    model.Settings.ModelUnitSystem = rhino3dm.UnitSystem.Feet\n    layers_by_classification_codes = {}\n\n    for code in classification_codes:\n        name = f'{code}-{constants.LAS_CLASSIFICATION_CODES&#091;str(code)]}'\n        point_cloud = rhino3dm.PointCloud()\n        props = {\n            'name': name,\n            'idx': 0,\n            'point_cloud': point_cloud,\n        }\n        layers_by_classification_codes&#091;str(code)] = props\n    \n    for x, y, z, code in pts:\n        pt = create_rhino3dm_point(x, y, z)\n        layers_by_classification_codes&#091;str(code)]&#091;'point_cloud'].Add(pt)\n    \n    for code, props in layers_by_classification_codes.items():\n        layer = rhino3dm.Layer()\n        layer.Name = props&#091;'name']\n        layer_idx = model.Layers.Add(layer)\n        attrs = rhino3dm.ObjectAttributes()\n        attrs.LayerIndex = layer_idx\n        model.Objects.Add(props&#091;'point_cloud'], attrs)\n    \n    model.Write(f'las_to_rhino_test.3dm', 7)<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">WhiteboxTools<\/h2>\n\n\n\n<p>Although lesser known, <a href=\"https:\/\/jblindsay.github.io\/ghrg\/software.shtml#WhiteboxTools\">WhiteboxTools<\/a> is a library that is &#8220;<a href=\"https:\/\/gis.stackexchange.com\/questions\/88322\/what-lidar-processing-tools-are-available-in-python\">written in Rust and has extensive support for\u00a0Python-based scripting<\/a>&#8220;.<\/p>\n\n\n\n<p>Where Laspy is great for it&#8217;s no nonsense read and write functions, WhiteboxTools offers a great range of extra functions for working with <em>.las<\/em> or <em>.laz<\/em> files.<\/p>\n\n\n\n<p>For example, you can color a lidar point set with an image (<a href=\"https:\/\/gis.stackexchange.com\/questions\/88322\/what-lidar-processing-tools-are-available-in-python\">code courtesy of WhiteboxTools<\/a>): <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from whitebox_tools import WhiteboxTools\n\nwbt = WhiteboxTools()\nwbt.work_dir = \"\/path\/to\/data\/\"\nin_lidar = \"lidar_data.las\"\nin_image = \"airphoto.tif\"\nout_lidar = \"colourized_lidar.las\"\nwbt.lidar_colourize(in_lidar, in_image, out_lidar) <\/code><\/pre>\n\n\n\n<p><a href=\"https:\/\/gis.stackexchange.com\/questions\/88322\/what-lidar-processing-tools-are-available-in-python\">Here&#8217;s the full list of the additional functions WhiteboxTools offers<\/a>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>BlockMaximum: Creates a block-maximum raster from an input LAS file.<\/li>\n\n\n\n<li>BlockMinimum: Creates a block-minimum raster from an input LAS file.<\/li>\n\n\n\n<li>FilterLidarScanAngles: Removes points in a LAS file with scan angles greater than a threshold.<\/li>\n\n\n\n<li>FindFlightlineEdgePoints: Identifies points along a flightline&#8217;s edge in a LAS file.<\/li>\n\n\n\n<li>FlightlineOverlap: Reads a LiDAR (LAS) point file and outputs a raster containing the number of overlapping flight lines in each grid cell.<\/li>\n\n\n\n<li>LidarElevationSlice: Outputs all of the points within a LiDAR (LAS) point file that lie between a specified elevation range.<\/li>\n\n\n\n<li>LasToAscii: Converts one or more LAS files into ASCII text files.<\/li>\n\n\n\n<li>LidarColourize: Adds the red-green-blue colour fields of a LiDAR (LAS) file based on an input image.<\/li>\n\n\n\n<li>LidarGroundPointFilter: Identifies ground points within LiDAR dataset.<\/li>\n\n\n\n<li>LidarIdwInterpolation: Interpolates LAS files using an inverse-distance weighted (IDW) scheme.<\/li>\n\n\n\n<li>LidarHillshade: Calculates a hillshade value for points within a LAS file and stores these data in the RGB field.<\/li>\n\n\n\n<li>LidarHistogram: Creates a histogram from LiDAR data.<\/li>\n\n\n\n<li>LidarInfo: Prints information about a LiDAR (LAS) dataset, including header, point return frequency, and classification data and information about the variable length records (VLRs) and geokeys.<\/li>\n\n\n\n<li>LidarJoin: Joins multiple LiDAR (LAS) files into a single LAS file.<\/li>\n\n\n\n<li>LidarKappaIndex: Performs a kappa index of agreement (KIA) analysis on the classifications of two LAS files.<\/li>\n\n\n\n<li>LidarNearestNeighbourGridding: Grids LAS files using nearest-neighbour scheme.<\/li>\n\n\n\n<li>LidarPointDensity: Calculates the spatial pattern of point density for a LiDAR data set.<\/li>\n\n\n\n<li>LidarPointStats: Creates several rasters summarizing the distribution of LAS point data.<\/li>\n\n\n\n<li>LidarRemoveDuplicates: Removes duplicate points from a LiDAR data set.<\/li>\n\n\n\n<li>LidarRemoveOutliers: Removes outliers (high and low points) in a LiDAR point cloud.<\/li>\n\n\n\n<li>LidarSegmentation: Segments a LiDAR point cloud based on normal vectors.<\/li>\n\n\n\n<li>LidarSegmentationBasedFilter: Identifies ground points within LiDAR point clouds using a segmentation based approach.<\/li>\n\n\n\n<li>LidarTile: Tiles a LiDAR LAS file into multiple LAS files.<\/li>\n\n\n\n<li>LidarTophatTransform: Performs a white top-hat transform on a Lidar dataset; as an estimate of height above ground, this is useful for modelling the vegetation canopy.<\/li>\n\n\n\n<li>NormalVectors: Calculates normal vectors for points within a LAS file and stores these data (XYZ vector components) in the RGB field.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Sure C and C++ have good libraries for processing <em>.las<\/em> and <em>.laz<\/em> files, but <strong>Laspy<\/strong> and <strong>WhiteboxTools<\/strong> offer integration with Numpy and great extra functions for processing your lidar data. So the next time you find yourself with a point cloud on your hands, give Laspy or WhiteboxTools a shot. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Obsessed with point clouds and Python? Me too! Recently I discovered that the USGS has tons of free lidar survey data available on The National Map. Free lidar data is great, but what&#8217;s the best way to extract the data in these files to suit your needs? There&#8217;s open source software available such as CloudCompare [&hellip;]<\/p>\n","protected":false},"author":13228,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-37","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blogs.oregonstate.edu\/schaefkr\/wp-json\/wp\/v2\/posts\/37","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.oregonstate.edu\/schaefkr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.oregonstate.edu\/schaefkr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.oregonstate.edu\/schaefkr\/wp-json\/wp\/v2\/users\/13228"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.oregonstate.edu\/schaefkr\/wp-json\/wp\/v2\/comments?post=37"}],"version-history":[{"count":1,"href":"https:\/\/blogs.oregonstate.edu\/schaefkr\/wp-json\/wp\/v2\/posts\/37\/revisions"}],"predecessor-version":[{"id":39,"href":"https:\/\/blogs.oregonstate.edu\/schaefkr\/wp-json\/wp\/v2\/posts\/37\/revisions\/39"}],"wp:attachment":[{"href":"https:\/\/blogs.oregonstate.edu\/schaefkr\/wp-json\/wp\/v2\/media?parent=37"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.oregonstate.edu\/schaefkr\/wp-json\/wp\/v2\/categories?post=37"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.oregonstate.edu\/schaefkr\/wp-json\/wp\/v2\/tags?post=37"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}