diff --git a/LASRead/LASFile.cs b/LASRead/LASFile.cs
index 1cf84ae..7d9c51b 100644
--- a/LASRead/LASFile.cs
+++ b/LASRead/LASFile.cs
@@ -69,14 +69,23 @@ namespace LASFormat
// As the header has now been read, we can now create some underlying streams, to act as our object sources.
// Set our current source position to the end of the header.
source.Position = Header.HeaderSize;
- long VLRSize = Header.DataOffset - Header.HeaderSize;
- VLRStream = GetOffsetStream(source, VLRSize);
+ //long VLRSize = Header.DataOffset - Header.HeaderSize;
+ //VLRStream = GetOffsetStream(source, VLRSize);
// Grab a starting VLR
- VLRStart = Header.HeaderSize;
+ //VLRStart = Header.HeaderSize;
VLRHeader initial = new VLRHeader();
- initial.ReadRecords(VLRStream);
- vlrCollection = new RecordCollection(ref VLRStream, VLRStart, Header.NumberVLRs, initial);
-
+ initial.ReadRecords(source);
+ vlrCollection = new RecordCollection(ref source, Header.HeaderSize, Header.NumberVLRs, initial);
+ // !---------------------------------------------------------------
+ // NB: at this point, the actual coordinate system is unknown - there is no obvious
+ // conversions for this data
+ // - An example are New Zealand coordinates, which are presented as Transverse Mercator easting and northing distances in meters,
+ // rather than Geodetic Datum latitude and longitude points in degrees
+ // To resolve coordinates on a world-wide scale, we'll need to identify the coordinate data type and convert to a
+ // standard coordinate system
+ // !---------------------------------------------------------------
+ // A VLR with a user ID of LASF_Projection\0 contains the required coordinate system:
+ // E.g.: 2016 scan information from LINZ contains the following transform string: 'NZGD2000 / New Zealand Transverse Mercator 2000 + NZVD2016 height|NZGD2000|NZVD2016 height|'
// Grab a starting PDR
PDRStart = Header.DataOffset;
source.Position = (long)PDRStart;
@@ -97,13 +106,13 @@ namespace LASFormat
points = Activator.CreateInstance(typeof(PDRCollection<>).MakeGenericType(PDRType), new object[] { Header, PDRStream, initialPoint});
PointsType = Type.GetTypeArray(new object[] { points })[0];
// Grab a starting EVLR
- EVLRStart = Header.StartOfFirstExtendedVLR;
- source.Position = (long)EVLRStart;
- long EVLRSize = (Header.StartOfWaveformDPR == 0 ? source.Length : (long)Header.StartOfWaveformDPR) - (long)Header.StartOfFirstExtendedVLR;
- EVLRStream = GetOffsetStream(source, EVLRSize);
+ //EVLRStart = Header.StartOfFirstExtendedVLR;
+ source.Position = Header.StartOfFirstExtendedVLR;
+ //long EVLRSize = (Header.StartOfWaveformDPR == 0 ? source.Length : (long)Header.StartOfWaveformDPR) - (long)Header.StartOfFirstExtendedVLR;
+ //EVLRStream = GetOffsetStream(source, EVLRSize);
EVLRHeader evlrInitial = new EVLRHeader();
- evlrInitial.ReadRecords(EVLRStream);
- evlrCollection = new RecordCollection(ref EVLRStream, EVLRStart, Header.NumberOfExtendedVLRs, evlrInitial);
+ evlrInitial.ReadRecords(source);
+ evlrCollection = new RecordCollection(ref EVLRStream, Header.StartOfFirstExtendedVLR, Header.NumberOfExtendedVLRs, evlrInitial);
// Finally, set the stream back to the starting position
source.Position = 0;
// TODO: Grab a starting Waveform
diff --git a/LASRead/LASFormat/DataHelpers.cs b/LASRead/LASFormat/DataHelpers.cs
index 9700bc8..e5f0437 100644
--- a/LASRead/LASFormat/DataHelpers.cs
+++ b/LASRead/LASFormat/DataHelpers.cs
@@ -49,6 +49,7 @@ namespace LASRead.LASFormat
for (int i = 0; i < length; i++)
{
characters[i] = (char)values[start + i];
+ if (characters[i] == 0) characters[i] = '-';
}
return characters;
}
diff --git a/LASRead/LASFormat/FileHeader.cs b/LASRead/LASFormat/FileHeader.cs
index 6f47131..cf875f7 100644
--- a/LASRead/LASFormat/FileHeader.cs
+++ b/LASRead/LASFormat/FileHeader.cs
@@ -172,7 +172,7 @@ namespace LASRead.LASFormat
/// New in 1.4
/// Start of the first Extended Variable Length Record (EVLR)
///
- public ulong StartOfFirstExtendedVLR { get; set; }
+ public long StartOfFirstExtendedVLR { get; set; }
///
/// New in 1.4
/// Number of Extended Variable Length Records
@@ -296,7 +296,7 @@ namespace LASRead.LASFormat
if (VersionMinor >= 4)
{
StartOfWaveformDPR = BitConverter.ToUInt64(inputHeader, 227);
- StartOfFirstExtendedVLR = BitConverter.ToUInt64(inputHeader, 235);
+ StartOfFirstExtendedVLR = (long)BitConverter.ToUInt64(inputHeader, 235);
NumberOfExtendedVLRs = BitConverter.ToUInt32(inputHeader, 243);
NumberPointRecords = BitConverter.ToUInt64(inputHeader, 247);
NumberPointsByReturn = DataHelpers.ToULongArray(inputHeader, 255, 120);
diff --git a/LASRead/LASFormat/GeoKeyTag.cs b/LASRead/LASFormat/GeoKeyTag.cs
new file mode 100644
index 0000000..f432d12
--- /dev/null
+++ b/LASRead/LASFormat/GeoKeyTag.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace LASRead.LASFormat
+{
+
+ class GeoKeyTag // Tag = 34735
+ {
+ // Header, first 8 bytes
+ short KeyDirectoryVersion;
+ short KeyRevision;
+ short MinorRevision;
+ short NumberOfKeys;
+
+
+
+ ///
+ /// GeoTIFF Tags that are included to contain the format (especially coordinate) data of the file
+ /// Due to the structure of a TIFF file, data are stored in Keys - these keys often further link to data in another tag
+ /// LiDAR capture data from a section of New Zealand contianed the following keys:
+ /// 1 1 0 11 -- ?
+ /// 1024 0 1 1 -- Type of coordinates (1 == Projected System)
+ /// 1025 0 1 1 -- Raster system used (1 == Pixel is area)
+ /// 1026 34737 66 0 -- ASCII Text about the file (Specifically, 'NZGD2000 / New Zealand Transverse Mercator 2000 + NZVD2016 height' - between 0-66 bytes of tag 34737)
+ /// 2049 34737 9 66 -- ASCII Geographic Citation Parameters, between 66-66+9 bytes ('NZGD2000')
+ /// 2054 0 1 9102 -- Angular Units (9102 = Degrees)
+ /// 3072 0 1 2193 -- Projected Coordinate System Type (2193 is not an expected value
+ /// 3076 0 1 9001 -- Projection Units (9001 = meters)
+ /// 4096 0 1 7839 -- Vertical Coordinate System (7839 = NZVD2016)
+ /// 4097 34737 16 75 -- ASCII Vertical System Name ('NZVD2016 height')
+ /// 4098 0 1 1169 -- Vertical Datum
+ /// 4099 0 1 9001 -- Vertical Units (meters)
+ ///
+ struct Key
+ {
+ public short KeyID;
+ public short TIFFTagLocation;
+ public short Count;
+ public short Value_Offset;
+ }
+
+
+
+ ///
+ /// Linear unit specifications - used in key ID 3076
+ ///
+ enum Units
+ {
+ Undefined = 0,
+ Linear_Meter = 9001,
+ Linear_Foot = 9002,
+ Linear_Foot_US_Survey = 9003,
+ Linear_Foot_Modified_American = 9004,
+ Linear_Foot_Clarke = 9005,
+ Linear_Foot_Indian = 9006,
+ Linear_Link = 9007,
+ Linear_Link_Benoit = 9008,
+ Linear_Link_Sears = 9009,
+ Linear_Chain_Benoit = 9010,
+ Linear_Chain_Sears = 9011,
+ Linear_Yard_Sears = 9012,
+ Linear_Yard_Indian = 9013,
+ Linear_Fathom = 9014,
+ Linear_Mile_International_Nautical = 9015,
+
+ Angular_Radian = 9101,
+ Angular_Degree = 9102,
+ Angular_Arc_Minute = 9103,
+ Angular_Arc_Second = 9104,
+ Angular_Grad = 9105,
+ Angular_Gon = 9106,
+ Angular_DMS = 9107,
+ Angular_DMS_Hemisphere = 9108,
+ }
+
+ ///
+ /// Vertical system codes - (key 4096)
+ ///
+ enum VerticalTypeCodes
+ {
+ VertCS_Airy_1830_ellipsoid = 5001,
+ VertCS_Airy_Modified_1849_ellipsoid = 5002,
+ VertCS_ANS_ellipsoid = 5003,
+ VertCS_Bessel_1841_ellipsoid = 5004,
+ VertCS_Bessel_Modified_ellipsoid = 5005,
+ VertCS_Bessel_Namibia_ellipsoid = 5006,
+ VertCS_Clarke_1858_ellipsoid = 5007,
+ VertCS_Clarke_1866_ellipsoid = 5008,
+ VertCS_Clarke_1880_Benoit_ellipsoid = 5010,
+ VertCS_Clarke_1880_IGN_ellipsoid = 5011,
+ VertCS_Clarke_1880_RGS_ellipsoid = 5012,
+ VertCS_Clarke_1880_Arc_ellipsoid = 5013,
+ VertCS_Clarke_1880_SGA_1922_ellipsoid = 5014,
+ VertCS_Everest_1830_1937_Adjustment_ellipsoid = 5015,
+ VertCS_Everest_1830_1967_Definition_ellipsoid = 5016,
+ VertCS_Everest_1830_1975_Definition_ellipsoid = 5017,
+ VertCS_Everest_1830_Modified_ellipsoid = 5018,
+ VertCS_GRS_1980_ellipsoid = 5019,
+ VertCS_Helmert_1906_ellipsoid = 5020,
+ VertCS_INS_ellipsoid = 5021,
+ VertCS_International_1924_ellipsoid = 5022,
+ VertCS_International_1967_ellipsoid = 5023,
+ VertCS_Krassowsky_1940_ellipsoid = 5024,
+ VertCS_NWL_9D_ellipsoid = 5025,
+ VertCS_NWL_10D_ellipsoid = 5026,
+ VertCS_Plessis_1817_ellipsoid = 5027,
+ VertCS_Struve_1860_ellipsoid = 5028,
+ VertCS_War_Office_ellipsoid = 5029,
+ VertCS_WGS_84_ellipsoid = 5030,
+ VertCS_GEM_10C_ellipsoid = 5031,
+ VertCS_OSU86F_ellipsoid = 5032,
+ VertCS_OSU91A_ellipsoid = 5033,
+ VertCS_Newlyn = 5101,
+ VertCS_North_American_Vertical_Datum_1929 = 5102,
+ VertCS_North_American_Vertical_Datum_1988 = 5103,
+ VertCS_Yellow_Sea_1956 = 5104,
+ VertCS_Baltic_Sea = 5105,
+ VertCS_Caspian_Sea = 5106,
+
+ NZ_Vertical = 7839
+ }
+ }
+}
diff --git a/LASRead/LASFormat/Record.cs b/LASRead/LASFormat/Record.cs
index 55753b0..03c4e28 100644
--- a/LASRead/LASFormat/Record.cs
+++ b/LASRead/LASFormat/Record.cs
@@ -39,7 +39,13 @@ namespace LASRead.LASFormat
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));
- sb.Append(header.ToString());
+ 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();
}
}
@@ -139,7 +145,7 @@ namespace LASRead.LASFormat
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: " + 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();
}
@@ -151,10 +157,10 @@ namespace LASRead.LASFormat
///
class EVLRHeader : VLRHeader
{
- public new const int headerLength = 60;
+ public const int headerLength = 60;
new public ulong RecordLengthAfterHeader { get; set; }
- new public int HeaderLength { get => 60; }
+ new public int HeaderLength { get => headerLength; }
public override bool ReadRecords(byte[] source)
{
@@ -198,7 +204,7 @@ namespace LASRead.LASFormat
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: " + 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();
}
diff --git a/LASRead/LASFormat/RecordCollection.cs b/LASRead/LASFormat/RecordCollection.cs
index 8f237e8..c66fe4a 100644
--- a/LASRead/LASFormat/RecordCollection.cs
+++ b/LASRead/LASFormat/RecordCollection.cs
@@ -9,7 +9,7 @@ namespace LASRead.LASFormat
public class RecordCollection : IEnumerable
{
RecordEnumerator enumerator;
- public RecordCollection(ref Stream source, ulong startPosition, uint maxItems, IRecordPayloadHeader firstHeader)
+ public RecordCollection(ref Stream source, long startPosition, uint maxItems, IRecordPayloadHeader firstHeader)
{
enumerator = new RecordEnumerator(ref source, startPosition, maxItems, firstHeader);
}
@@ -27,19 +27,22 @@ namespace LASRead.LASFormat
public class RecordEnumerator : IEnumerator
{
Stream dataSource;
- ulong streamStart;
- ulong currentPosition;
+ long streamStart;
+ long currentPosition;
uint currentCount;
uint maxCount;
+ IRecordPayloadHeader evalHeader;
- public RecordEnumerator(ref Stream source, ulong startPosition, uint maxItems, IRecordPayloadHeader firstHeader)
+ public RecordEnumerator(ref Stream source, long startPosition, uint maxItems, IRecordPayloadHeader firstHeader)
{
- dataSource = source;
- streamStart = startPosition;
- currentPosition = startPosition;
- currentCount = 0;
- maxCount = maxItems;
- Current = new Record(firstHeader, (long)startPosition);
+ // Recieves the stream reference, the start position of the first record header, the number of record headers
+ dataSource = source; // Stream
+ streamStart = startPosition; // start of the first header
+ currentPosition = startPosition; // The current position of the enumerator
+ currentCount = 0; // Total number of items enumerated
+ maxCount = maxItems; // Maximum number of items to enumerate
+ evalHeader = firstHeader;
+ //Current = new Record(firstHeader, startPosition);
}
object IEnumerator.Current => Current;
@@ -64,9 +67,16 @@ namespace LASRead.LASFormat
else
{
long oldPos = dataSource.Position;
- currentPosition = (ulong)Current.header.HeaderLength + currentPosition;
- dataSource.Position = (long)currentPosition;
- Record nextRecord = new Record(Current.header.ParseRecord(dataSource), (long)currentPosition);
+ // Advance the position in the stream to the beginning of the next record, after the header and data
+ if (Current != null)
+ {
+ currentPosition += Current.header.HeaderLength + Current.header.RecordLengthAfterHeader;
+ }
+
+ dataSource.Position = currentPosition;
+ evalHeader.ReadRecords(dataSource);
+ Record nextRecord = new Record(evalHeader, currentPosition);
+ nextRecord.ReadData(dataSource);
Current = nextRecord;
dataSource.Position = oldPos;
currentCount++;