using System; using System.Collections.Generic; using System.IO; using System.Text; namespace LASRead.LASFormat { public class Record { readonly byte[] data; public readonly IRecordPayloadHeader header; readonly long position; /// /// Creates a new Variable Length Record /// /// The parsed header of the object /// The position of this header in the source stream public Record(IRecordPayloadHeader header, long position) { this.header = header; data = new byte[header.RecordLengthAfterHeader]; this.position = position; } /// /// Reads the data payload associated with this header /// /// The source stream /// A bool representing success or failed public bool ReadData(Stream s) { long pos = s.Position; s.Position = position + header.HeaderLength; s.Read(data, 0, header.RecordLengthAfterHeader); s.Position = pos; return true; } public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append(header.ToString() + Environment.NewLine); sb.Append(string.Format("Binary Data @{0}, of length {1}{2}", position + header.HeaderLength, header.HeaderLength, Environment.NewLine)); for (int i = 0; i < data.Length; i += 2) { if (i % 8 == 0) sb.Append("\n"); sb.Append($"{BitConverter.ToUInt16(data[i..])} "); } //sb.Append(Environment.NewLine + new string(DataHelpers.ToCharArray(data))); return sb.ToString(); } } public interface IRecordPayloadHeader { int HeaderLength { get; } ushort Reserved { get; set; } byte[] Id { get; set; } ushort RecordID { get; set; } ushort RecordLengthAfterHeader { get; set; } byte[] Description { get; set; } IPointDataRecord Payload { get; set; } bool VerifyRecord(byte[] source); bool ReadRecords(byte[] source); bool ReadRecords(Stream source); IRecordPayloadHeader ParseRecord(byte[] source); IRecordPayloadHeader ParseRecord(Stream source); } class VLRHeader : IRecordPayloadHeader { public const int headerLength = 54; public ushort Reserved { get; set; } public byte[] Id { get; set; } public ushort RecordID { get; set; } public ushort RecordLengthAfterHeader { get; set; } public byte[] Description { get; set; } public IPointDataRecord Payload { get; set; } public int HeaderLength { get => headerLength; } public bool VerifyRecord(byte[] source) { if (BitConverter.ToUInt16(source, 0) != 0) { return false; } return true; } public virtual bool ReadRecords(byte[] source) { if (source.Length != 54) { return false; } Reserved = BitConverter.ToUInt16(source, 0); Id = new byte[16]; Array.Copy(source, 2, Id, 0, 16); RecordID = BitConverter.ToUInt16(source, 18); RecordLengthAfterHeader = BitConverter.ToUInt16(source, 20); Description = new byte[32]; Array.Copy(source, 22, Description, 0, 32); return true; } public bool ReadRecords(Stream source) { byte[] bytes = new byte[headerLength]; source.Read(bytes, 0, headerLength); ReadRecords(bytes); return true; } public virtual byte[] GetRecords() { byte[] bytes = new byte[54]; bytes[0] = 0; bytes[1] = 0; Id.CopyTo(bytes, 2); BitConverter.GetBytes(RecordID).CopyTo(bytes, 18); BitConverter.GetBytes(RecordLengthAfterHeader).CopyTo(bytes, 20); Description.CopyTo(bytes, 22); return bytes; } public virtual IRecordPayloadHeader ParseRecord(byte[] source) { VLRHeader newHeader = new VLRHeader(); newHeader.ReadRecords(source); return newHeader; } public virtual IRecordPayloadHeader ParseRecord(Stream source) { byte[] bytes = new byte[headerLength]; source.Read(bytes, 0, headerLength); return ParseRecord(bytes); } public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append("Reserved: " + Reserved.ToString() + Environment.NewLine); sb.Append("User ID: " + new string(DataHelpers.ToCharArray(Id)) + Environment.NewLine); sb.Append("Record ID: " + RecordID.ToString() + Environment.NewLine); sb.Append("Record Length After Header: " + RecordLengthAfterHeader.ToString() + Environment.NewLine); sb.Append("Description: " + new string(DataHelpers.ToCharArray(Description)) + Environment.NewLine); return sb.ToString(); } } /// /// Extended Variable Length Records differ from VLRs in the maximum data that can be saved, and header size /// class EVLRHeader : VLRHeader { public const int headerLength = 60; new public ulong RecordLengthAfterHeader { get; set; } new public int HeaderLength { get => headerLength; } public override bool ReadRecords(byte[] source) { if (source.Length != 60) { return false; } Reserved = BitConverter.ToUInt16(source, 0); Id = new byte[16]; Array.Copy(source, 2, Id, 0, 16); RecordID = BitConverter.ToUInt16(source, 18); RecordLengthAfterHeader = BitConverter.ToUInt64(source, 20); Description = new byte[32]; Array.Copy(source, 28, Description, 0, 32); return true; } public override byte[] GetRecords() { byte[] bytes = new byte[60]; bytes[0] = 0; bytes[1] = 0; Id.CopyTo(bytes, 2); BitConverter.GetBytes(RecordID).CopyTo(bytes, 18); BitConverter.GetBytes(RecordLengthAfterHeader).CopyTo(bytes, 20); Description.CopyTo(bytes, 28); return bytes; } public override IRecordPayloadHeader ParseRecord(byte[] source) { EVLRHeader newHeader = new EVLRHeader(); newHeader.ReadRecords(source); return newHeader; } public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append("Reserved: " + Reserved.ToString() + Environment.NewLine); sb.Append("User ID: " + new string(DataHelpers.ToCharArray(Id)) + Environment.NewLine); sb.Append("Record ID: " + RecordID.ToString() + Environment.NewLine); sb.Append("Record Length After Header: " + RecordLengthAfterHeader.ToString() + Environment.NewLine); sb.Append("Description: " + new string(DataHelpers.ToCharArray(Description)) + Environment.NewLine); return sb.ToString(); } } }