Added some notes, basic key reading

This commit is contained in:
Brychan Dempsey 2021-11-17 16:34:33 +13:00
parent 9335d37f7c
commit a7d04f54da
6 changed files with 182 additions and 32 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -172,7 +172,7 @@ namespace LASRead.LASFormat
/// <b>New in 1.4</b><br />
/// Start of the first Extended Variable Length Record (EVLR)
/// </summary>
public ulong StartOfFirstExtendedVLR { get; set; }
public long StartOfFirstExtendedVLR { get; set; }
/// <summary>
/// <b>New in 1.4</b><br />
/// 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);

View File

@ -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;
/// <summary>
/// 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)
/// </summary>
struct Key
{
public short KeyID;
public short TIFFTagLocation;
public short Count;
public short Value_Offset;
}
/// <summary>
/// Linear unit specifications - used in key ID 3076
/// </summary>
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,
}
/// <summary>
/// Vertical system codes - (key 4096)
/// </summary>
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
}
}
}

View File

@ -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
/// </summary>
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();
}

View File

@ -9,7 +9,7 @@ namespace LASRead.LASFormat
public class RecordCollection : IEnumerable<Record>
{
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<Record>
{
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++;