From c966dfe4e9fee22baf4a97601462fafd1b5e6283 Mon Sep 17 00:00:00 2001 From: Brychan Dempsey Date: Tue, 16 Nov 2021 14:02:05 +1300 Subject: [PATCH] Added comments, replaced private fields with auto properties --- LASRead/LASFile.cs | 19 +++- LASRead/LASFormat/FileHeader.cs | 167 ++++++++++++-------------------- LASRead/UnitTest1.cs | 23 +++++ 3 files changed, 101 insertions(+), 108 deletions(-) create mode 100644 LASRead/UnitTest1.cs diff --git a/LASRead/LASFile.cs b/LASRead/LASFile.cs index 557197f..119b3a9 100644 --- a/LASRead/LASFile.cs +++ b/LASRead/LASFile.cs @@ -12,13 +12,22 @@ namespace LASFormat { public class LASFile : IDisposable { + // based on the LAS format specifications from asprs - The imaging and Geospatial Information Society - https://www.asprs.org/divisions-committees/lidar-division/laser-las-file-format-exchange-activities + // Parses LAS files, such that the data is in a more readily-usable format + // + // Todos: + // - Split a LAS file into subsections, probably about 100m^2 + // - generate heightmaps from sections, 0-255 per point + // - Output heightmaps in a Unity- and Unreal- ready format + // - Ouput vertex colours for each point, if available + // - Allow exporting different layers of a file private const int V = 16384; private const int V1 = V * (ushort.MaxValue +1); // File Structure (v1.4): - // FileHeader - // VLRs (Variable Length Records) - // PDRs (Point Data Records) + // File Header (a.k.a. Public Header Block) + // VLRs (Variable Length Records) - Any number of these (max 65535 bytes) + // PDRs (Point Data Records) - Any number of // EVLRs (Extended VRLs) Stream source; Stream VLRStream; @@ -44,14 +53,14 @@ namespace LASFormat public Type PDRType { get; private set; } /// - /// Opens the .las file from the provided stream + /// Opens the LAS file from the provided stream /// /// public LASFile(Stream source) { this.source = source; Header = new FileHeader(); - Header.ReadHeader(source); + Header.ReadHeader(source); // Reads intrinsics from the stream, such as PDRType = Type.GetType("LASRead.LASFormat.PDR" + Header.PointDataRecordFormat.ToString()); diff --git a/LASRead/LASFormat/FileHeader.cs b/LASRead/LASFormat/FileHeader.cs index 95f3488..c92319b 100644 --- a/LASRead/LASFormat/FileHeader.cs +++ b/LASRead/LASFormat/FileHeader.cs @@ -39,53 +39,14 @@ namespace LASRead.LASFormat } public class FileHeader { - ushort headerSize; - // Follows the correct order of the header - char[] fileSignature; - ushort fileSourceID; - ushort globalEncoding; - // Optional Fields are filled with zero - uint GUIDData1 = 0; - ushort GUIDData2 = 0; - ushort GUIDData3 = 0; - byte[] GUIDData4 = new byte[] {0,0,0,0,0,0,0,0}; - byte versionMajor; - byte versionMinor; - char[] systemIdentifier; - char[] generatingSoftware; - ushort fileCreationDayOfYear; - ushort fileCreationYear; - uint dataOffset; - uint numberVLRs; // Variable Length Records - byte pointDataRecordFormat; - ushort pointDataRecordLength; - uint legacyNumberOfPointRecords; - uint[] legacyNumberOfPointByReturn; - double x_scaleFactor; - double y_scaleFactor; - double z_scaleFactor; - double x_offset; - double y_offset; - double z_offset; - double x_max; - double x_min; - double y_max; - double y_min; - double z_max; - double z_min; - ulong startOfWaveformDPR; // Data Packet Record - ulong startOfFirstExtendedVLR; - uint numberOfExtendedVLRs; - ulong numberPointRecords; - ulong[] numberPointsByReturn; /// /// Signature of the data. Should always be "LASF" /// - public char[] FileSignature { get => fileSignature; set => fileSignature = value; } + public char[] FileSignature { get; set; } /// /// ID Associated with the data. Intended to differentiate between different sources /// - public ushort FileSourceID { get => fileSourceID; set => fileSourceID = value; } + public ushort FileSourceID { get; set; } /// /// Flags Indicating the formats of certain data
/// 0 : GPS Time Format (0 - Week time; 1 - standard GPS Time)
@@ -95,161 +56,161 @@ namespace LASRead.LASFormat /// 4 : Coordinate Reference System is WKT, else GeoTIFF
/// 5-15 : Reserved
///
- public ushort GlobalEncoding { get => globalEncoding; set => globalEncoding = value; } - public uint GUIDData11 { get => GUIDData1; set => GUIDData1 = value; } - public ushort GUIDData21 { get => GUIDData2; set => GUIDData2 = value; } - public ushort GUIDData31 { get => GUIDData3; set => GUIDData3 = value; } - public byte[] GUIDData41 { get => GUIDData4; set => GUIDData4 = value; } - public byte VersionMajor { get => versionMajor; set => versionMajor = value; } - public byte VersionMinor { get => versionMinor; set => versionMinor = value; } + public ushort GlobalEncoding { get; set; } + public uint GUIDData11 { get; set; } = 0; + public ushort GUIDData21 { get; set; } = 0; + public ushort GUIDData31 { get; set; } = 0; + public byte[] GUIDData41 { get; set; } = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; + public byte VersionMajor { get; set; } + public byte VersionMinor { get; set; } /// /// Identifies the hardware used to collect the data
/// Can be MERGE, MODIFICATION, EXTRACTION, TRANSFORMATION, OTHER, or a custom expression ///
- public char[] SystemIdentifier { get => systemIdentifier; set => systemIdentifier = value; } + public char[] SystemIdentifier { get; set; } /// /// Identifies the software used to encode the data /// - public char[] GeneratingSoftware { get => generatingSoftware; set => generatingSoftware = value; } + public char[] GeneratingSoftware { get; set; } /// /// The day of the year this file was created /// - public ushort FileCreationDayOfYear { get => fileCreationDayOfYear; set => fileCreationDayOfYear = value; } + public ushort FileCreationDayOfYear { get; set; } /// /// The year this data was collected /// - public ushort FileCreationYear { get => fileCreationYear; set => fileCreationYear = value; } + public ushort FileCreationYear { get; set; } /// /// The size of this header /// - public ushort HeaderSize { get => headerSize; set => headerSize = value; } + public ushort HeaderSize { get; set; } /// /// The offset of the first Point-Data-Record (PDR) /// - public uint DataOffset { get => dataOffset; set => dataOffset = value; } + public uint DataOffset { get; set; } /// /// The number of Variable Length Records (VLRs) present /// - public uint NumberVLRs { get => numberVLRs; set => numberVLRs = value; } + public uint NumberVLRs { get; set; } /// /// An integer representing the format of the PDR data.
/// See ///
- public byte PointDataRecordFormat { get => pointDataRecordFormat; set => pointDataRecordFormat = value; } + public byte PointDataRecordFormat { get; set; } /// /// The length of a PDR record /// - public ushort PointDataRecordLength { get => pointDataRecordLength; set => pointDataRecordLength = value; } + public ushort PointDataRecordLength { get; set; } /// /// Number of point records for legacy compatibility (32 bits) /// - public uint LegacyNumberOfPointRecords { get => legacyNumberOfPointRecords; set => legacyNumberOfPointRecords = value; } + public uint LegacyNumberOfPointRecords { get; set; } /// /// todo /// - public uint[] LegacyNumberOfPointByReturn { get => legacyNumberOfPointByReturn; set => legacyNumberOfPointByReturn = value; } + public uint[] LegacyNumberOfPointByReturn { get; set; } /// /// Scale factor of the X axis. Multiply by this value to get the true value /// - public double X_scaleFactor { get => x_scaleFactor; set => x_scaleFactor = value; } + public double X_scaleFactor { get; set; } /// /// Scale factor of the Y axis. Multiply by this value to get the true value /// - public double Y_scaleFactor { get => y_scaleFactor; set => y_scaleFactor = value; } + public double Y_scaleFactor { get; set; } /// /// Scale factor of the Z axis. Multiply by this value to get the true value /// - public double Z_scaleFactor { get => z_scaleFactor; set => z_scaleFactor = value; } + public double Z_scaleFactor { get; set; } /// /// Offset of the X axis.
/// Calculate Coordinates like so:
/// X_final = X * X_scaleFactor + X_offset ///
- public double X_offset { get => x_offset; set => x_offset = value; } + public double X_offset { get; set; } /// /// Offset of the Y axis.
/// Calculate Coordinates like so:
/// Y_final = Y * Y_scaleFactor + Y_offset ///
- public double Y_offset { get => y_offset; set => y_offset = value; } + public double Y_offset { get; set; } /// /// Offset of the Z axis.
/// Calculate Coordinates like so:
/// Z_final = Z * Z_scaleFactor + Z_offset ///
- public double Z_offset { get => z_offset; set => z_offset = value; } + public double Z_offset { get; set; } /// /// Largest X value in the data /// - public double X_max { get => x_max; set => x_max = value; } + public double X_max { get; set; } /// /// Smallest X value in the data /// - public double X_min { get => x_min; set => x_min = value; } + public double X_min { get; set; } /// /// Largest Y value in the data /// - public double Y_max { get => y_max; set => y_max = value; } + public double Y_max { get; set; } /// /// Smallest Y value in the data /// - public double Y_min { get => y_min; set => y_min = value; } + public double Y_min { get; set; } /// /// Largest Z value in the data /// - public double Z_max { get => z_max; set => z_max = value; } + public double Z_max { get; set; } /// /// Smallest Z value in the data /// - public double Z_min { get => z_min; set => z_min = value; } + public double Z_min { get; set; } /// /// New in 1.4
/// Start of the Waveform Data Packet Records ///
- public ulong StartOfWaveformDPR { get => startOfWaveformDPR; set => startOfWaveformDPR = value; } + public ulong StartOfWaveformDPR { get; set; } /// /// New in 1.4
/// Start of the first Extended Variable Length Record (EVLR) ///
- public ulong StartOfFirstExtendedVLR { get => startOfFirstExtendedVLR; set => startOfFirstExtendedVLR = value; } + public ulong StartOfFirstExtendedVLR { get; set; } /// /// New in 1.4
/// Number of Extended Variable Length Records ///
- public uint NumberOfExtendedVLRs { get => numberOfExtendedVLRs; set => numberOfExtendedVLRs = value; } + public uint NumberOfExtendedVLRs { get; set; } /// /// New in 1.4
/// Number of point records (64 bits) ///
- public ulong NumberPointRecords { get => numberPointRecords; set => numberPointRecords = value; } + public ulong NumberPointRecords { get; set; } /// /// New in 1.4
/// todo ///
- public ulong[] NumberPointsByReturn { get => numberPointsByReturn; set => numberPointsByReturn = value; } + public ulong[] NumberPointsByReturn { get; set; } public byte[] GetAsByteArray() { - byte[] endBytes = new byte[headerSize]; - DataHelpers.ToByteArray(fileSignature).CopyTo(endBytes,0); //4 - BitConverter.GetBytes(fileSourceID).CopyTo(endBytes, 4); // 2 - BitConverter.GetBytes(globalEncoding).CopyTo(endBytes, 6); // 2 - BitConverter.GetBytes(GUIDData1).CopyTo(endBytes, 8); // 4 - BitConverter.GetBytes(GUIDData2).CopyTo(endBytes, 12); // 2 - BitConverter.GetBytes(GUIDData3).CopyTo(endBytes, 14); // 2 - GUIDData4.CopyTo(endBytes, 16); // 8 - endBytes[24] = versionMajor; - endBytes[25] = versionMinor; - DataHelpers.ToByteArray(systemIdentifier).CopyTo(endBytes, 26); // 32 Bytes + byte[] endBytes = new byte[HeaderSize]; + DataHelpers.ToByteArray(FileSignature).CopyTo(endBytes,0); //4 + BitConverter.GetBytes(FileSourceID).CopyTo(endBytes, 4); // 2 + BitConverter.GetBytes(GlobalEncoding).CopyTo(endBytes, 6); // 2 + BitConverter.GetBytes(GUIDData11).CopyTo(endBytes, 8); // 4 + BitConverter.GetBytes(GUIDData21).CopyTo(endBytes, 12); // 2 + BitConverter.GetBytes(GUIDData31).CopyTo(endBytes, 14); // 2 + GUIDData41.CopyTo(endBytes, 16); // 8 + endBytes[24] = VersionMajor; + endBytes[25] = VersionMinor; + DataHelpers.ToByteArray(SystemIdentifier).CopyTo(endBytes, 26); // 32 Bytes DataHelpers.ToByteArray(GeneratingSoftware).CopyTo(endBytes, 58); // 32 Bytes - BitConverter.GetBytes(fileCreationDayOfYear).CopyTo(endBytes, 90); // 2 - BitConverter.GetBytes(fileCreationYear).CopyTo(endBytes, 92); // 2 - BitConverter.GetBytes(headerSize).CopyTo(endBytes, 94); // 2 - BitConverter.GetBytes(dataOffset).CopyTo(endBytes, 96); // 4 - BitConverter.GetBytes(numberVLRs).CopyTo(endBytes, 100); // 4 - endBytes[104] = pointDataRecordFormat; - BitConverter.GetBytes(pointDataRecordLength).CopyTo(endBytes, 105); // 2 - BitConverter.GetBytes(legacyNumberOfPointRecords).CopyTo(endBytes, 107); // 4 + BitConverter.GetBytes(FileCreationDayOfYear).CopyTo(endBytes, 90); // 2 + BitConverter.GetBytes(FileCreationYear).CopyTo(endBytes, 92); // 2 + BitConverter.GetBytes(HeaderSize).CopyTo(endBytes, 94); // 2 + BitConverter.GetBytes(DataOffset).CopyTo(endBytes, 96); // 4 + BitConverter.GetBytes(NumberVLRs).CopyTo(endBytes, 100); // 4 + endBytes[104] = PointDataRecordFormat; + BitConverter.GetBytes(PointDataRecordLength).CopyTo(endBytes, 105); // 2 + BitConverter.GetBytes(LegacyNumberOfPointRecords).CopyTo(endBytes, 107); // 4 DataHelpers.ToByteArray(LegacyNumberOfPointByReturn).CopyTo(endBytes, 111); // 20 bytes BitConverter.GetBytes(X_scaleFactor).CopyTo(endBytes, 131); // 8 BitConverter.GetBytes(Y_scaleFactor).CopyTo(endBytes, 139); // 8 @@ -265,7 +226,7 @@ namespace LASRead.LASFormat BitConverter.GetBytes(Z_min).CopyTo(endBytes, 219); // 8 if (VersionMajor >= 1 && VersionMinor >= 4) { - BitConverter.GetBytes(startOfWaveformDPR).CopyTo(endBytes, 227); // 8 + BitConverter.GetBytes(StartOfWaveformDPR).CopyTo(endBytes, 227); // 8 BitConverter.GetBytes(StartOfFirstExtendedVLR).CopyTo(endBytes, 235); // 8 BitConverter.GetBytes(NumberOfExtendedVLRs).CopyTo(endBytes, 243); // 4 BitConverter.GetBytes(NumberPointRecords).CopyTo(endBytes, 247); // 8 @@ -283,8 +244,8 @@ namespace LASRead.LASFormat source.Position = 94; byte[] headerSizeBytes = new byte[2]; source.Read(headerSizeBytes, 0, 2); - headerSize = BitConverter.ToUInt16(headerSizeBytes, 0); - if (headerSize < 375) + HeaderSize = BitConverter.ToUInt16(headerSizeBytes, 0); + if (HeaderSize < 375) { // Assert the version is less than 1.4, where records after z-min were added. source.Position = 24; @@ -297,7 +258,7 @@ namespace LASRead.LASFormat } source.Position = 0; - byte[] inputHeader = new byte[headerSize]; + byte[] inputHeader = new byte[HeaderSize]; source.Read(inputHeader, 0, HeaderSize); FileSignature = new char[] { (char)inputHeader[0], (char)inputHeader[1], (char)inputHeader[2], (char)inputHeader[3] }; @@ -325,19 +286,19 @@ namespace LASRead.LASFormat Z_scaleFactor = BitConverter.ToDouble(inputHeader, 147); X_offset = BitConverter.ToDouble(inputHeader, 155); Y_offset = BitConverter.ToDouble(inputHeader, 163); - z_offset = BitConverter.ToDouble(inputHeader, 171); + Z_offset = BitConverter.ToDouble(inputHeader, 171); X_max = BitConverter.ToDouble(inputHeader, 179); X_min = BitConverter.ToDouble(inputHeader, 187); Y_max = BitConverter.ToDouble(inputHeader, 195); Y_min = BitConverter.ToDouble(inputHeader, 203); Z_max = BitConverter.ToDouble(inputHeader, 211); Z_min = BitConverter.ToDouble(inputHeader, 219); - if (versionMinor >= 4) + if (VersionMinor >= 4) { StartOfWaveformDPR = BitConverter.ToUInt64(inputHeader, 227); StartOfFirstExtendedVLR = BitConverter.ToUInt64(inputHeader, 235); NumberOfExtendedVLRs = BitConverter.ToUInt32(inputHeader, 243); - numberPointRecords = BitConverter.ToUInt64(inputHeader, 247); + NumberPointRecords = BitConverter.ToUInt64(inputHeader, 247); NumberPointsByReturn = DataHelpers.ToULongArray(inputHeader, 255, 120); } return true; @@ -392,7 +353,7 @@ namespace LASRead.LASFormat sb.Append("Y Min: " + Y_min.ToString() + Environment.NewLine); sb.Append("Z Max: " + Z_max.ToString() + Environment.NewLine); sb.Append("Z Min: " + Z_min.ToString() + Environment.NewLine); - if (versionMinor >= 4) + if (VersionMinor >= 4) { sb.Append("Waveform Start: " + StartOfWaveformDPR.ToString() + Environment.NewLine); sb.Append("Extended VLR Start: " + StartOfFirstExtendedVLR.ToString() + Environment.NewLine); diff --git a/LASRead/UnitTest1.cs b/LASRead/UnitTest1.cs new file mode 100644 index 0000000..dbe98ff --- /dev/null +++ b/LASRead/UnitTest1.cs @@ -0,0 +1,23 @@ +using NUnit.Framework; +using LASFormat; +using System.IO; + +namespace Tests +{ + public class Tests + { + [SetUp] + public void Setup() + { + } + + [Test] + public void Test1() + { + FileStream fs = File.OpenRead("C:\\points.las"); + LASFile lasFile = new LASFile(fs); + + Assert.Pass(); + } + } +} \ No newline at end of file