Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 21 additions & 155 deletions LasZipNetStandard/LasPoint.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using System;
using System.Runtime.InteropServices;
using System;
using System.Runtime.CompilerServices;

namespace Kuoste.LasZipNetStandard
{

public class LasPoint
{
public double X { get; set; }
Expand All @@ -23,24 +22,25 @@ public class LasPoint
public ushort Green { get; set; }
public ushort Blue { get; set; }

internal static void ConvertPoint(LasZipPointStruct pointStruct, ref LasPoint lasPoint)
{
lasPoint.X = pointStruct.X;
lasPoint.Y = pointStruct.Y;
lasPoint.Z = pointStruct.Z;
lasPoint.Intensity = pointStruct.Intensity;
lasPoint.ReturnNumber = pointStruct.ReturnNumber;
lasPoint.NumberOfReturns = pointStruct.NumberOfReturns;
lasPoint.ScanDirectionFlag = pointStruct.ScanDirectionFlag;
lasPoint.EdgeOfFlightLine = pointStruct.EdgeOfFlightLine;
lasPoint.Classification = pointStruct.Classification;
lasPoint.ScanAngleRank = pointStruct.ScanAngleRank;
lasPoint.UserData = pointStruct.UserData;
lasPoint.PointSourceId = pointStruct.PointSourceID;
lasPoint.GpsTime = pointStruct.GpsTime;
lasPoint.Red = pointStruct.Rgb[0];
lasPoint.Green = pointStruct.Rgb[1];
lasPoint.Blue = pointStruct.Rgb[2];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe void ConvertPoint(LasZipPointNative* pointStruct, ref LasPoint lasPoint, double scaleFactorX, double scaleFactorY, double scaleFactorZ, double offsetX, double offsetY, double offsetZ)
{
lasPoint.X = pointStruct->X * scaleFactorX + offsetX;
lasPoint.Y = pointStruct->Y * scaleFactorY + offsetY;
lasPoint.Z = pointStruct->Z * scaleFactorZ + offsetZ;
lasPoint.Intensity = pointStruct->Intensity;
lasPoint.ReturnNumber = pointStruct->ReturnNumber;
lasPoint.NumberOfReturns = pointStruct->NumberOfReturns;
lasPoint.ScanDirectionFlag = pointStruct->ScanDirectionFlag;
lasPoint.EdgeOfFlightLine = pointStruct->EdgeOfFlightLine;
lasPoint.Classification = pointStruct->Classification;
lasPoint.ScanAngleRank = pointStruct->ScanAngleRank;
lasPoint.UserData = pointStruct->UserData;
lasPoint.PointSourceId = pointStruct->PointSourceID;
lasPoint.GpsTime = pointStruct->GpsTime;
lasPoint.Red = pointStruct->Rgb[0];
lasPoint.Green = pointStruct->Rgb[1];
lasPoint.Blue = pointStruct->Rgb[2];
}

public override bool Equals(object obj)
Expand Down Expand Up @@ -74,138 +74,4 @@ public override int GetHashCode()
return HashCode.Combine(GpsTime, Z);
}
}

[StructLayout(LayoutKind.Sequential)]
public struct LasZipPointStruct
{
public int X;
public int Y;
public int Z;
public ushort Intensity;
private byte bitField1; // ReturnNumber : 3, NumberOfReturns : 3, ScanDirectionFlag : 1, EdgeOfFlightLine : 1
private byte bitField2; // Classification : 5, SyntheticFlag : 1, KeypointFlag : 1, WithheldFlag : 1
public sbyte ScanAngleRank;
public byte UserData;
public ushort PointSourceID;

// LAS 1.4 only
public short ExtendedScanAngle;
private byte bitField3; // ExtendedPointType : 2, ExtendedScannerChannel : 2, ExtendedClassificationFlags : 4
public byte ExtendedClassification;
private byte bitField4; // ExtendedReturnNumber : 4, ExtendedNumberOfReturns : 4

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)]
public byte[] Dummy;

public double GpsTime;

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public ushort[] Rgb;

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 29)]
public byte[] WavePacket;

public int NumExtraBytes;

public IntPtr ExtraBytes; // Use IntPtr instead of byte* for safe pointer handling

public byte ReturnNumber
{
get => (byte)(bitField1 & 0b00000111);
set => bitField1 = (byte)((bitField1 & 0b11111000) | (value & 0b00000111));
}

public byte NumberOfReturns
{
get => (byte)((bitField1 >> 3) & 0b00000111);
set => bitField1 = (byte)((bitField1 & 0b11000111) | ((value & 0b00000111) << 3));
}

public byte ScanDirectionFlag
{
get => (byte)((bitField1 >> 6) & 0b00000001);
set => bitField1 = (byte)((bitField1 & 0b10111111) | ((value & 0b00000001) << 6));
}

public byte EdgeOfFlightLine
{
get => (byte)((bitField1 >> 7) & 0b00000001);
set => bitField1 = (byte)((bitField1 & 0b01111111) | ((value & 0b00000001) << 7));
}

public byte Classification
{
get => (byte)(bitField2 & 0b00011111);
set => bitField2 = (byte)((bitField2 & 0b11100000) | (value & 0b00011111));
}

public byte SyntheticFlag
{
get => (byte)((bitField2 >> 5) & 0b00000001);
set => bitField2 = (byte)((bitField2 & 0b11011111) | ((value & 0b00000001) << 5));
}

public byte KeypointFlag
{
get => (byte)((bitField2 >> 6) & 0b00000001);
set => bitField2 = (byte)((bitField2 & 0b10111111) | ((value & 0b00000001) << 6));
}

public byte WithheldFlag
{
get => (byte)((bitField2 >> 7) & 0b00000001);
set => bitField2 = (byte)((bitField2 & 0b01111111) | ((value & 0b00000001) << 7));
}

public byte ExtendedPointType
{
get => (byte)(bitField3 & 0b00000011);
set => bitField3 = (byte)((bitField3 & 0b11111100) | (value & 0b00000011));
}

public byte ExtendedScannerChannel
{
get => (byte)((bitField3 >> 2) & 0b00000011);
set => bitField3 = (byte)((bitField3 & 0b11110011) | ((value & 0b00000011) << 2));
}

public byte ExtendedClassificationFlags
{
get => (byte)((bitField3 >> 4) & 0b00001111);
set => bitField3 = (byte)((bitField3 & 0b00001111) | ((value & 0b00001111) << 4));
}

public byte ExtendedReturnNumber
{
get => (byte)(bitField4 & 0b00001111);
set => bitField4 = (byte)((bitField4 & 0b11110000) | (value & 0b00001111));
}

public byte ExtendedNumberOfReturns
{
get => (byte)((bitField4 >> 4) & 0b00001111);
set => bitField4 = (byte)((bitField4 & 0b00001111) | ((value & 0b00001111) << 4));
}

internal static LasZipPointStruct ConvertPoint(LasPoint lasPoint)
{
return new LasZipPointStruct()
{
X = (int)(lasPoint.X + 0.5),
Y = (int)(lasPoint.Y + 0.5),
Z = (int)(lasPoint.Z + 0.5),
Intensity = lasPoint.Intensity,
ReturnNumber = lasPoint.ReturnNumber,
NumberOfReturns = lasPoint.NumberOfReturns,
ScanDirectionFlag = lasPoint.ScanDirectionFlag,
EdgeOfFlightLine = lasPoint.EdgeOfFlightLine,
Classification = lasPoint.Classification,
ScanAngleRank = lasPoint.ScanAngleRank,
UserData = lasPoint.UserData,
PointSourceID = lasPoint.PointSourceId,
GpsTime = lasPoint.GpsTime,
Rgb = new ushort[] { lasPoint.Red, lasPoint.Green, lasPoint.Blue, 0 }
};
}
}
}
81 changes: 62 additions & 19 deletions LasZipNetStandard/LasZip.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace Kuoste.LasZipNetStandard
{
public class LasZip
public class LasZip : IDisposable
{
private bool _disposed = false;
private const string _lasZipDll = "laszip64";

private IntPtr _pLasZipReader;
Expand Down Expand Up @@ -135,7 +137,8 @@ public void SetWriterHeader(LaszipHeaderStruct header)
/// </summary>
/// <param name="point"> Point data is read to this reference. </param>
/// <exception cref="Exception"> Reading failed. </exception>
public void ReadPoint(ref LasPoint point)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ReadPoint(ref LasPoint point)
{
// Get the memory location for the point in LasZip library
if (_pPointReader == IntPtr.Zero)
Expand All @@ -152,22 +155,18 @@ public void ReadPoint(ref LasPoint point)
throw new Exception("Failed to read LasZip point");
}

// Copy point data from C++ struct to C# class
LasPoint.ConvertPoint(Marshal.PtrToStructure<LasZipPointStruct>(_pPointReader), ref point);

// Scale the coordinates and add offsets. Not using the laszip_get_coordinates in order to make things faster.
point.X = point.X * _headerReader.ScaleFactorX + _headerReader.OffsetX;
point.Y = point.Y * _headerReader.ScaleFactorY + _headerReader.OffsetY;
point.Z = point.Z * _headerReader.ScaleFactorZ + _headerReader.OffsetZ;
LasPoint.ConvertPoint((LasZipPointNative*)_pPointReader, ref point,
_headerReader.ScaleFactorX, _headerReader.ScaleFactorY, _headerReader.ScaleFactorZ,
_headerReader.OffsetX, _headerReader.OffsetY, _headerReader.OffsetZ);
}

/// <summary>
/// Writes the point given as reference.
/// </summary>
/// <param name="point"> LasPoint to write. Note that the method changes the coordinates
/// of the point by applying the Offsets and ScaleFactors. </param>
/// <param name="point"> LasPoint to write. The coordinates are scaled and offset
/// using the header values before writing. </param>
/// <exception cref="Exception"> Writing failed. </exception>
public void WritePoint(ref LasPoint point)
public unsafe void WritePoint(ref LasPoint point)
{
// Get the memory location for the point in LasZip library
if (_pPointWriter == IntPtr.Zero)
Expand All @@ -178,12 +177,9 @@ public void WritePoint(ref LasPoint point)
}
}

// Scale the coordinates and add offsets. Not using the laszip_set_coordinates in order to make things faster.
point.X = (point.X - _headerWriter.OffsetX) / _headerWriter.ScaleFactorX;
point.Y = (point.Y - _headerWriter.OffsetY) / _headerWriter.ScaleFactorY;
point.Z = (point.Z - _headerWriter.OffsetZ) / _headerWriter.ScaleFactorZ;

Marshal.StructureToPtr(LasZipPointStruct.ConvertPoint(point), _pPointWriter, false);
LasZipPointNative.WriteFromLasPoint((LasZipPointNative*)_pPointWriter, point,
_headerWriter.ScaleFactorX, _headerWriter.ScaleFactorY, _headerWriter.ScaleFactorZ,
_headerWriter.OffsetX, _headerWriter.OffsetY, _headerWriter.OffsetZ);

// Write point
if (laszip_write_point(_pLasZipWriter) != 0)
Expand Down Expand Up @@ -232,5 +228,52 @@ public void DestroyWriter()

_pLasZipWriter = IntPtr.Zero;
}

/// <summary>
/// Releases all resources used by the LasZip instance.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

/// <summary>
/// Releases the unmanaged resources and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">True to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;

// Clean up unmanaged resources
if (_pLasZipReader != IntPtr.Zero)
{
laszip_close_reader(_pLasZipReader);
laszip_destroy(_pLasZipReader);
_pLasZipReader = IntPtr.Zero;
_pPointReader = IntPtr.Zero;
}

if (_pLasZipWriter != IntPtr.Zero)
{
laszip_close_writer(_pLasZipWriter);
laszip_destroy(_pLasZipWriter);
_pLasZipWriter = IntPtr.Zero;
_pPointWriter = IntPtr.Zero;
_pHeaderWriter = IntPtr.Zero;
}

_disposed = true;
}

/// <summary>
/// Finalizer to ensure unmanaged resources are released.
/// </summary>
~LasZip()
{
Dispose(false);
}
}
}
1 change: 1 addition & 0 deletions LasZipNetStandard/LasZipNetStandard.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<RootNamespace>Kuoste.LasZipNetStandard</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
Expand Down
Loading
Loading