diff --git a/components/fpvectorial/lasvectorialreader.pas b/components/fpvectorial/lasvectorialreader.pas index e4daaaa29c..d8a02111c8 100644 --- a/components/fpvectorial/lasvectorialreader.pas +++ b/components/fpvectorial/lasvectorialreader.pas @@ -31,22 +31,23 @@ uses fpvectorial; type - // LAS data types - laschar = Char; // or ShortInt + // LAS data types introduced in LAS 1.0 + laschar = AnsiChar; // or ShortInt lasuchar = Byte; lasshort = Smallint; lasushort = Word; laslong = Integer; lasulong = Cardinal; + lasdouble = double; + // laslonglong = Int64; lasulonglong = QWord; - lasdouble = double; - // PUBLIC HEADER BLOCK - TLASPublicHeaderBlock = packed record + // PUBLIC HEADER BLOCK version 1.0 + TLASPublicHeaderBlock_1_0 = packed record FileSignatureLASF: array[0..3] of laschar; - FileSourceID: lasushort; - GlobalEncoding: lasushort; + FileSourceID: lasushort; // Reserved in LAS 1.0 + GlobalEncoding: lasushort; // Reserved in LAS 1.0 ProjectIDGUIDdata1: lasulong; // Optional ProjectIDGUIDdata2: lasushort; // Optional ProjectIDGUIDdata3: lasushort; // Optional @@ -55,38 +56,87 @@ type VersionMinor: lasuchar; SystemIdentifier: array[0..31] of laschar; GeneratingSoftware: array[0..31] of laschar; - FileCreationDayofYear: lasushort; - FileCreationYear: lasushort; -{Number of Variable Length Records -Point Data Format ID (0-99 for spec) -Point Data Record Length -Number of point records -Number of points by return -X scale factor -Y scale factor -Z scale factor -X offset -Y offset -Z offset -Max X -Min X -Max Y -Min Y -Max Z -Min Z -Start of Waveform Data Packet Record -Any field in the Public Header Block that is not required and is not used must be zero filled.} + FileCreationDayofYear: lasushort; // Name in LAS 1.0 -> FlightDateJulian, but same meaning + FileCreationYear: lasushort; // Name in LAS 1.0 -> Year, but same meaning + HeaderSize: lasushort; // Name in LAS 1.0 -> OffsetToData + OffsetToPointData: lasulong; + NumberofVariableLengthRecords: lasulong; + PointDataFormatID: lasuchar; // (0-99 for spec) + PointDataRecordLength: lasushort; + Numberofpointrecords: lasulong; + Numberofpointsbyreturn: array[0..4] of lasulong; + Xscalefactor: lasdouble; + Yscalefactor: lasdouble; + Zscalefactor: lasdouble; + + Xoffset: lasdouble; + Yoffset: lasdouble; + Zoffset: lasdouble; + MaxX: lasdouble; + MinX: lasdouble; + MaxY: lasdouble; + MinY: lasdouble; + MaxZ: lasdouble; + MinZ: lasdouble; + end; + + // PUBLIC HEADER BLOCK Extension in Version 1.3 + TLASPublicHeaderBlock_1_3_Extension = packed record + StartofWaveformDataPacket: lasulonglong; + end; + + TLASVariableLengthRecord = packed record + RecordSignatureAABB: lasushort; + UserID: array[0..15] of laschar; + RecordID: lasushort; + RecordLengthAfterHeader: lasushort; + Description: array[0..31] of laschar; + end; + + TLASPointDataRecordFormat0 = packed record + X: laslong; + Y: laslong; + Z: laslong; + Intensity: lasushort; + Flags: Byte; + Classification: lasuchar; + ScanAngleRank: lasuchar; // (-90 to +90) - is the left side + FileMarker: lasuchar; + UserBitField: lasushort; + end; + + TLASPointDataRecordFormat1 = packed record + X: laslong; + Y: laslong; + Z: laslong; + Intensity: lasushort; + Flags: Byte; + Classification: lasuchar; + ScanAngleRank: lasuchar; // (-90 to +90) - is the left side + FileMarker: lasuchar; + UserBitField: lasushort; + GPSTime: lasdouble; end; { TvLASVectorialReader } TvLASVectorialReader = class(TvCustomVectorialReader) private + // Stream position information + InitialPos, PositionAfterPublicHeader: Int64; {$ifdef FPVECTORIALDEBUG_LAS} procedure DebugOutPublicHeaderBlock(); {$endif} + procedure ReadVariableLengthRecords(AStream: TStream); public - PublicHeaderBlock: TLASPublicHeaderBlock; + // Public Header + PublicHeaderBlock_1_0: TLASPublicHeaderBlock_1_0; + PublicHeaderBlock_1_3_Extension: TLASPublicHeaderBlock_1_3_Extension; + // Variable Length Records + VariableLengthRecords: array of TLASVariableLengthRecord; + // Point Data + PointsFormat0: array of TLASPointDataRecordFormat0; + PointsFormat1: array of TLASPointDataRecordFormat1; { General reading methods } procedure ReadFromStream(AStream: TStream; AData: TvVectorialDocument); override; end; @@ -97,33 +147,116 @@ implementation procedure TvLASVectorialReader.DebugOutPublicHeaderBlock; begin WriteLn(Format('FileSignatureLASF = %s = %x %x %x %x', - [PublicHeaderBlock.FileSignatureLASF, - PublicHeaderBlock.FileSignatureLASF[0], - PublicHeaderBlock.FileSignatureLASF[1], - PublicHeaderBlock.FileSignatureLASF[2], - PublicHeaderBlock.FileSignatureLASF[3]])); - WriteLn(Format('FileSourceID = %x', [PublicHeaderBlock.FileSourceID])); - WriteLn(Format('GlobalEncoding = %x', [PublicHeaderBlock.GlobalEncoding])); - WriteLn(Format('ProjectIDGUIDdata1 = %x', [PublicHeaderBlock.ProjectIDGUIDdata1])); - WriteLn(Format('ProjectIDGUIDdata2 = %x', [PublicHeaderBlock.ProjectIDGUIDdata2])); - WriteLn(Format('ProjectIDGUIDdata3 = %x', [PublicHeaderBlock.ProjectIDGUIDdata3])); -// WriteLn(Format('ProjectIDGUIDdata2 = %x', [ProjectIDGUIDdata2])); - WriteLn(Format('VersionMajor = %x', [PublicHeaderBlock.VersionMajor])); - WriteLn(Format('VersionMinor = %x', [PublicHeaderBlock.VersionMinor])); - WriteLn(Format('SystemIdentifier = %s', [PublicHeaderBlock.SystemIdentifier])); - WriteLn(Format('GeneratingSoftware = %s', [PublicHeaderBlock.GeneratingSoftware])); - WriteLn(Format('FileCreationDayofYear = %x', [PublicHeaderBlock.FileCreationDayofYear])); - WriteLn(Format('FileCreationYear = %x', [PublicHeaderBlock.FileCreationYear])); + [string(PublicHeaderBlock_1_0.FileSignatureLASF), + Ord(PublicHeaderBlock_1_0.FileSignatureLASF[0]), + Ord(PublicHeaderBlock_1_0.FileSignatureLASF[1]), + Ord(PublicHeaderBlock_1_0.FileSignatureLASF[2]), + Ord(PublicHeaderBlock_1_0.FileSignatureLASF[3])])); + WriteLn(Format('FileSourceID = $%x', [PublicHeaderBlock_1_0.FileSourceID])); + WriteLn(Format('GlobalEncoding = $%x', [PublicHeaderBlock_1_0.GlobalEncoding])); + WriteLn(Format('ProjectIDGUIDdata1 = $%x', [PublicHeaderBlock_1_0.ProjectIDGUIDdata1])); + WriteLn(Format('ProjectIDGUIDdata2 = $%x', [PublicHeaderBlock_1_0.ProjectIDGUIDdata2])); + WriteLn(Format('ProjectIDGUIDdata3 = $%x', [PublicHeaderBlock_1_0.ProjectIDGUIDdata3])); +// WriteLn(Format('ProjectIDGUIDdata4 = %x', [ProjectIDGUIDdata2])); + WriteLn(Format('VersionMajor = %d', [PublicHeaderBlock_1_0.VersionMajor])); + WriteLn(Format('VersionMinor = %d', [PublicHeaderBlock_1_0.VersionMinor])); + WriteLn(Format('SystemIdentifier = %s', [PublicHeaderBlock_1_0.SystemIdentifier])); + WriteLn(Format('GeneratingSoftware = %s', [PublicHeaderBlock_1_0.GeneratingSoftware])); + WriteLn(Format('FileCreationDayofYear = %d', [PublicHeaderBlock_1_0.FileCreationDayofYear])); + WriteLn(Format('FileCreationYear = %d', [PublicHeaderBlock_1_0.FileCreationYear])); + WriteLn(Format('HeaderSize = $%x', [PublicHeaderBlock_1_0.HeaderSize])); + WriteLn(Format('OffsetToPointData = $%x', [PublicHeaderBlock_1_0.OffsetToPointData])); + WriteLn(Format('NumberofVariableLengthRecords = $%x', [PublicHeaderBlock_1_0.NumberofVariableLengthRecords])); + WriteLn(Format('PointDataFormatID = $%x', [PublicHeaderBlock_1_0.PointDataFormatID])); + WriteLn(Format('PointDataRecordLength = $%x', [PublicHeaderBlock_1_0.PointDataRecordLength])); + WriteLn(Format('Numberofpointrecords = $%x', [PublicHeaderBlock_1_0.Numberofpointrecords])); + WriteLn(Format('Numberofpointsbyreturn = %x %x %x %x %x', + [PublicHeaderBlock_1_0.Numberofpointsbyreturn[0], + PublicHeaderBlock_1_0.Numberofpointsbyreturn[1], + PublicHeaderBlock_1_0.Numberofpointsbyreturn[2], + PublicHeaderBlock_1_0.Numberofpointsbyreturn[3], + PublicHeaderBlock_1_0.Numberofpointsbyreturn[4] + ])); + WriteLn(Format('Xscalefactor = %f', [PublicHeaderBlock_1_0.Xscalefactor])); + WriteLn(Format('Yscalefactor = %f', [PublicHeaderBlock_1_0.Yscalefactor])); + WriteLn(Format('Zscalefactor = %f', [PublicHeaderBlock_1_0.Zscalefactor])); + WriteLn(Format('Xoffset = %f', [PublicHeaderBlock_1_0.Xoffset])); + WriteLn(Format('Yoffset = %f', [PublicHeaderBlock_1_0.Yoffset])); + WriteLn(Format('Zoffset = %f', [PublicHeaderBlock_1_0.Zoffset])); + WriteLn(Format('MaxX = %f', [PublicHeaderBlock_1_0.MaxX])); + WriteLn(Format('MinX = %f', [PublicHeaderBlock_1_0.MinX])); + WriteLn(Format('MaxY = %f', [PublicHeaderBlock_1_0.MaxY])); + WriteLn(Format('MinY = %f', [PublicHeaderBlock_1_0.MinY])); + WriteLn(Format('MaxZ = %f', [PublicHeaderBlock_1_0.MaxZ])); + WriteLn(Format('MinZ = %f', [PublicHeaderBlock_1_0.MinZ])); + WriteLn(''); + WriteLn(Format('LAS 1.0 header size = $%x', [SizeOf(TLASPublicHeaderBlock_1_0)])); + WriteLn(''); end; {$endif} +procedure TvLASVectorialReader.ReadVariableLengthRecords(AStream: TStream); +var + i: Integer; + NextPosition: Int64; +begin + NextPosition := PositionAfterPublicHeader; + + SetLength(VariableLengthRecords, PublicHeaderBlock_1_0.NumberofVariableLengthRecords); + for i := 0 to PublicHeaderBlock_1_0.NumberofVariableLengthRecords - 1 do + begin + AStream.Position := NextPosition; + {$ifdef FPVECTORIALDEBUG_LAS} + WriteLn(Format('Variable Length Record #%d Position = $%x', [i, NextPosition])); + WriteLn(''); + {$endif} + AStream.Read(VariableLengthRecords[i], SizeOf(TLASVariableLengthRecord)); + NextPosition := AStream.Position+VariableLengthRecords[i].RecordLengthAfterHeader; + {$ifdef FPVECTORIALDEBUG_LAS} + WriteLn(Format('RecordSignatureAABB = $%x', [VariableLengthRecords[i].RecordSignatureAABB])); + WriteLn(Format('UserID = %s', [VariableLengthRecords[i].UserID])); + WriteLn(Format('RecordID = $%x', [VariableLengthRecords[i].RecordID])); + WriteLn(Format('RecordLengthAfterHeader = $%x', [VariableLengthRecords[i].RecordLengthAfterHeader])); + WriteLn(Format('Description = %s', [VariableLengthRecords[i].Description])); + WriteLn(''); + {$endif} + end; +end; + procedure TvLASVectorialReader.ReadFromStream(AStream: TStream; AData: TvVectorialDocument); begin - AStream.Read(PublicHeaderBlock, SizeOf(TLASPublicHeaderBlock)); + // First read the header like if it was for LAS 1.0, + // this will tell us the real version so then we read it again + InitialPos := AStream.Position; + AStream.Read(PublicHeaderBlock_1_0, SizeOf(TLASPublicHeaderBlock_1_0)); {$ifdef FPVECTORIALDEBUG_LAS} DebugOutPublicHeaderBlock(); {$endif} + + // First check the signature + if PublicHeaderBlock_1_0.FileSignatureLASF <> 'LASF' then + raise Exception.Create('[TvLASVectorialReader.ReadFromStream] Invalid file signoture while reading LAS file'); + + // In LAS 1.3+ read the header extension + // ToDo + + PositionAfterPublicHeader := AStream.Position; + + // Read the variable length records + ReadVariableLengthRecords(AStream); + + // Read the point data + AStream.Position := InitialPos + PublicHeaderBlock_1_0.OffsetToPointData; + case PublicHeaderBlock_1_0.PointDataFormatID of + 0: + begin + + end; + 1: + begin + end; + end; end; initialization