Initial files
This commit is contained in:
commit
f430800a45
63
.gitattributes
vendored
Normal file
63
.gitattributes
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
###############################################################################
|
||||||
|
# Set default behavior to automatically normalize line endings.
|
||||||
|
###############################################################################
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set default behavior for command prompt diff.
|
||||||
|
#
|
||||||
|
# This is need for earlier builds of msysgit that does not have it on by
|
||||||
|
# default for csharp files.
|
||||||
|
# Note: This is only used by command line
|
||||||
|
###############################################################################
|
||||||
|
#*.cs diff=csharp
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set the merge driver for project and solution files
|
||||||
|
#
|
||||||
|
# Merging from the command prompt will add diff markers to the files if there
|
||||||
|
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||||
|
# the diff markers are never inserted). Diff markers may cause the following
|
||||||
|
# file extensions to fail to load in VS. An alternative would be to treat
|
||||||
|
# these files as binary and thus will always conflict and require user
|
||||||
|
# intervention with every merge. To do so, just uncomment the entries below
|
||||||
|
###############################################################################
|
||||||
|
#*.sln merge=binary
|
||||||
|
#*.csproj merge=binary
|
||||||
|
#*.vbproj merge=binary
|
||||||
|
#*.vcxproj merge=binary
|
||||||
|
#*.vcproj merge=binary
|
||||||
|
#*.dbproj merge=binary
|
||||||
|
#*.fsproj merge=binary
|
||||||
|
#*.lsproj merge=binary
|
||||||
|
#*.wixproj merge=binary
|
||||||
|
#*.modelproj merge=binary
|
||||||
|
#*.sqlproj merge=binary
|
||||||
|
#*.wwaproj merge=binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# behavior for image files
|
||||||
|
#
|
||||||
|
# image files are treated as binary by default.
|
||||||
|
###############################################################################
|
||||||
|
#*.jpg binary
|
||||||
|
#*.png binary
|
||||||
|
#*.gif binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# diff behavior for common document formats
|
||||||
|
#
|
||||||
|
# Convert binary document formats to text before diffing them. This feature
|
||||||
|
# is only available from the command line. Turn it on by uncommenting the
|
||||||
|
# entries below.
|
||||||
|
###############################################################################
|
||||||
|
#*.doc diff=astextplain
|
||||||
|
#*.DOC diff=astextplain
|
||||||
|
#*.docx diff=astextplain
|
||||||
|
#*.DOCX diff=astextplain
|
||||||
|
#*.dot diff=astextplain
|
||||||
|
#*.DOT diff=astextplain
|
||||||
|
#*.pdf diff=astextplain
|
||||||
|
#*.PDF diff=astextplain
|
||||||
|
#*.rtf diff=astextplain
|
||||||
|
#*.RTF diff=astextplain
|
340
.gitignore
vendored
Normal file
340
.gitignore
vendored
Normal file
@ -0,0 +1,340 @@
|
|||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!?*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
*- Backup*.rdl
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
.idea/
|
||||||
|
*.sln.iml
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
healthchecksdb
|
37
LASRead.sln
Normal file
37
LASRead.sln
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.30611.23
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LASRead", "LASRead\LASRead.csproj", "{6E6BF63D-11CA-4F6B-8864-FC21A6E9244C}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{AB8F159A-BAB1-4CE0-AC0A-392733381916}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LasInteractor", "PrintLasData\LasInteractor.csproj", "{BEEFD9AE-42F8-472E-82E6-CE1A8A115243}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{6E6BF63D-11CA-4F6B-8864-FC21A6E9244C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{6E6BF63D-11CA-4F6B-8864-FC21A6E9244C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{6E6BF63D-11CA-4F6B-8864-FC21A6E9244C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6E6BF63D-11CA-4F6B-8864-FC21A6E9244C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{AB8F159A-BAB1-4CE0-AC0A-392733381916}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{AB8F159A-BAB1-4CE0-AC0A-392733381916}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{AB8F159A-BAB1-4CE0-AC0A-392733381916}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{AB8F159A-BAB1-4CE0-AC0A-392733381916}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{BEEFD9AE-42F8-472E-82E6-CE1A8A115243}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{BEEFD9AE-42F8-472E-82E6-CE1A8A115243}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{BEEFD9AE-42F8-472E-82E6-CE1A8A115243}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{BEEFD9AE-42F8-472E-82E6-CE1A8A115243}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {A730301B-3D24-4CAC-B787-5E811AD58068}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
9
LASRead/App.xaml
Normal file
9
LASRead/App.xaml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<Application x:Class="LASRead.App"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="clr-namespace:LASRead"
|
||||||
|
StartupUri="MainWindow.xaml">
|
||||||
|
<Application.Resources>
|
||||||
|
|
||||||
|
</Application.Resources>
|
||||||
|
</Application>
|
17
LASRead/App.xaml.cs
Normal file
17
LASRead/App.xaml.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Configuration;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace LASRead
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for App.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
10
LASRead/AssemblyInfo.cs
Normal file
10
LASRead/AssemblyInfo.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
[assembly: ThemeInfo(
|
||||||
|
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// or application resource dictionaries)
|
||||||
|
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// app, or any theme specific resource dictionaries)
|
||||||
|
)]
|
210
LASRead/LASFile.cs
Normal file
210
LASRead/LASFile.cs
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
using LASRead;
|
||||||
|
using LASRead.LASFormat;
|
||||||
|
using Microsoft.Windows.Themes;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Security.AccessControl;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace LASFormat
|
||||||
|
{
|
||||||
|
public class LASFile : IDisposable
|
||||||
|
{
|
||||||
|
private const int V = 16384;
|
||||||
|
private const int V1 = V * (ushort.MaxValue +1);
|
||||||
|
|
||||||
|
// File Structure (v1.4):
|
||||||
|
// FileHeader
|
||||||
|
// VLRs (Variable Length Records)
|
||||||
|
// PDRs (Point Data Records)
|
||||||
|
// EVLRs (Extended VRLs)
|
||||||
|
Stream source;
|
||||||
|
Stream VLRStream;
|
||||||
|
Stream PDRStream;
|
||||||
|
Stream EVLRStream;
|
||||||
|
|
||||||
|
|
||||||
|
FileHeader header;
|
||||||
|
|
||||||
|
|
||||||
|
ulong VLRStart;
|
||||||
|
ulong PDRStart;
|
||||||
|
ulong EVLRStart;
|
||||||
|
ulong WaveformStart;
|
||||||
|
|
||||||
|
public RecordCollection vlrCollection;
|
||||||
|
public RecordCollection evlrCollection;
|
||||||
|
public dynamic points;
|
||||||
|
|
||||||
|
public FileHeader Header { get => header; private set => header = value; }
|
||||||
|
|
||||||
|
public Type PointsType { get; private set; }
|
||||||
|
public Type PDRType { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens the .las file from the provided stream
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source"></param>
|
||||||
|
public LASFile(Stream source)
|
||||||
|
{
|
||||||
|
this.source = source;
|
||||||
|
Header = new FileHeader();
|
||||||
|
Header.ReadHeader(source);
|
||||||
|
|
||||||
|
PDRType = Type.GetType("LASRead.LASFormat.PDR" + Header.PointDataRecordFormat.ToString());
|
||||||
|
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
// Grab a starting VLR
|
||||||
|
VLRStart = Header.HeaderSize;
|
||||||
|
VLRHeader initial = new VLRHeader();
|
||||||
|
initial.ReadRecords(VLRStream);
|
||||||
|
vlrCollection = new RecordCollection(VLRStream, VLRStart, Header.NumberVLRs, initial);
|
||||||
|
|
||||||
|
// Grab a starting PDR
|
||||||
|
PDRStart = Header.DataOffset;
|
||||||
|
source.Position = (long)PDRStart;
|
||||||
|
long PDRSize = ((Header.VersionMajor >= 1 && Header.VersionMinor >= 4) ? (long)Header.StartOfFirstExtendedVLR : source.Length) - (long)PDRStart;
|
||||||
|
PDRStream = GetOffsetStream(source, PDRSize);
|
||||||
|
byte[] pdrInitial = new byte[Header.PointDataRecordLength];
|
||||||
|
PDRStream.Read(pdrInitial, 0, Header.PointDataRecordLength);
|
||||||
|
// Convert the PDRF to our PDR types. Uses System.Reflections to find the value and avoid the use of switch-case.
|
||||||
|
// Use generics to identify the PDR type.
|
||||||
|
Type t = Type.GetType("LASRead.LASFormat.PDR" + Header.PointDataRecordFormat.ToString());
|
||||||
|
IPointDataRecord initialPoint = (IPointDataRecord)Activator.CreateInstance(t);
|
||||||
|
initialPoint.ReadPoint(pdrInitial);
|
||||||
|
// Using generics to dynamically create a PDRCollection<PDR*> storage
|
||||||
|
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);
|
||||||
|
EVLRHeader evlrInitial = new EVLRHeader();
|
||||||
|
evlrInitial.ReadRecords(EVLRStream);
|
||||||
|
evlrCollection = new RecordCollection(EVLRStream, EVLRStart, Header.NumberOfExtendedVLRs, evlrInitial);
|
||||||
|
// Finally, set the stream back to the starting position
|
||||||
|
source.Position = 0;
|
||||||
|
// TODO: Grab a starting Waveform
|
||||||
|
// WaveformStart = Header.StartOfWaveformDPR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a new stream of the segment from the provided stream.<br />
|
||||||
|
/// Prefers to exist as a <see cref="MemoryStream"/>, though will create a temporary file
|
||||||
|
/// if the data is too large to store in memory.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">The stream to read from</param>
|
||||||
|
/// <param name="segmentSize">The total size of the data</param>
|
||||||
|
/// <param name="memoryLimit">The maximum memory we allocate</param>
|
||||||
|
/// <param name="bufferSize">The size of the buffer</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private Stream GetOffsetStream(Stream source, long segmentSize, int memoryLimit, int bufferSize)
|
||||||
|
{
|
||||||
|
long dataStart = source.Position;
|
||||||
|
Stream newStream;
|
||||||
|
if (segmentSize <= memoryLimit)
|
||||||
|
{
|
||||||
|
newStream = new MemoryStream((int)segmentSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string fileName = Path.GetTempFileName();
|
||||||
|
FileInfo fileInfo = new FileInfo(fileName);
|
||||||
|
newStream = fileInfo.Create(FileMode.OpenOrCreate, FileSystemRights.FullControl, FileShare.Read, V, FileOptions.DeleteOnClose, null);
|
||||||
|
}
|
||||||
|
byte[] buffer = new byte[bufferSize];
|
||||||
|
while (source.Position - dataStart + bufferSize <= segmentSize)
|
||||||
|
{
|
||||||
|
source.Read(buffer);
|
||||||
|
newStream.Write(buffer);
|
||||||
|
}
|
||||||
|
int remaining = (int)segmentSize - (int)(source.Position - dataStart);
|
||||||
|
if (remaining > 0)
|
||||||
|
{
|
||||||
|
source.Read(buffer, 0, remaining);
|
||||||
|
newStream.Write(buffer, 0, remaining);
|
||||||
|
}
|
||||||
|
return newStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream GetOffsetStream(Stream source, long segmentSize)
|
||||||
|
{
|
||||||
|
return GetOffsetStream(source, segmentSize, V1, V);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new .las file
|
||||||
|
/// </summary>
|
||||||
|
public LASFile()
|
||||||
|
{
|
||||||
|
source = null;
|
||||||
|
header = new FileHeader();
|
||||||
|
VLRStream = new MemoryStream();
|
||||||
|
PDRStream = new MemoryStream();
|
||||||
|
EVLRStream = new MemoryStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
header = null;
|
||||||
|
if (source != null)
|
||||||
|
{
|
||||||
|
source.Dispose();
|
||||||
|
}
|
||||||
|
if (VLRStream != null)
|
||||||
|
{
|
||||||
|
VLRStream.Dispose();
|
||||||
|
}
|
||||||
|
if (PDRStream != null)
|
||||||
|
{
|
||||||
|
PDRStream.Dispose();
|
||||||
|
}
|
||||||
|
if (EVLRStream != null)
|
||||||
|
{
|
||||||
|
EVLRStream.Dispose();
|
||||||
|
}
|
||||||
|
if (vlrCollection != null)
|
||||||
|
{
|
||||||
|
vlrCollection.GetEnumerator().Dispose();
|
||||||
|
vlrCollection = null;
|
||||||
|
}
|
||||||
|
if (evlrCollection != null)
|
||||||
|
{
|
||||||
|
evlrCollection.GetEnumerator().Dispose();
|
||||||
|
evlrCollection = null;
|
||||||
|
}
|
||||||
|
if (points != null)
|
||||||
|
{
|
||||||
|
points = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LASFile_v12
|
||||||
|
{
|
||||||
|
// File Structure (v1.2):
|
||||||
|
// FileHeader
|
||||||
|
// VLRs (Variable Length Records)
|
||||||
|
// PDRs (Point Data Records)
|
||||||
|
Stream source;
|
||||||
|
FileHeader header;
|
||||||
|
VLRHeader vlrs;
|
||||||
|
|
||||||
|
public LASFile VerifyVersion()
|
||||||
|
{
|
||||||
|
if (header.VersionMajor >= 1 && header.VersionMinor > 2)
|
||||||
|
{
|
||||||
|
return new LASFile(source);
|
||||||
|
}
|
||||||
|
else return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
172
LASRead/LASFormat/DataHelpers.cs
Normal file
172
LASRead/LASFormat/DataHelpers.cs
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace LASRead.LASFormat
|
||||||
|
{
|
||||||
|
static class DataHelpers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converts an array of characters into an array of bytes. Trims values > 255 to 255.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="characters"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static byte[] ToByteArray(char[] characters)
|
||||||
|
{
|
||||||
|
byte[] outBytes = new byte[characters.Length];
|
||||||
|
for (int i = 0; i < characters.Length; i++)
|
||||||
|
{
|
||||||
|
outBytes[i] = (byte)Math.Min((ushort)255, characters[i]);
|
||||||
|
}
|
||||||
|
return outBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] ToByteArray(uint[] values)
|
||||||
|
{
|
||||||
|
byte[] outBytes = new byte[4 * values.Length];
|
||||||
|
for (int i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
byte[] tBytes = BitConverter.GetBytes(values[i]);
|
||||||
|
tBytes.CopyTo(outBytes, i * 4);
|
||||||
|
}
|
||||||
|
return outBytes;
|
||||||
|
}
|
||||||
|
public static byte[] ToByteArray(ulong[] values)
|
||||||
|
{
|
||||||
|
byte[] outBytes = new byte[8 * values.Length];
|
||||||
|
for (int i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
byte[] tBytes = BitConverter.GetBytes(values[i]);
|
||||||
|
tBytes.CopyTo(outBytes, i * 8);
|
||||||
|
}
|
||||||
|
return outBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static char[] ToCharArray(byte[] values, int start, int length)
|
||||||
|
{
|
||||||
|
char[] characters = new char[length];
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
characters[i] = (char)values[start + i];
|
||||||
|
}
|
||||||
|
return characters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static char[] ToCharArray(byte[] values)
|
||||||
|
{
|
||||||
|
return ToCharArray(values, 0, values.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static uint[] ToUintArray(byte[] values, int start, int length)
|
||||||
|
{
|
||||||
|
uint[] tUints = new uint[length / 4];
|
||||||
|
for (int i = 0; i < length / 4; i++)
|
||||||
|
{
|
||||||
|
tUints[i] = BitConverter.ToUInt32(values, start + i * 4);
|
||||||
|
}
|
||||||
|
return tUints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ulong[] ToULongArray(byte[] values, int start, int length)
|
||||||
|
{
|
||||||
|
ulong[] tULongs = new ulong[length / 8];
|
||||||
|
|
||||||
|
for (int i = 0; i < length / 8; i++)
|
||||||
|
{
|
||||||
|
tULongs[i] = BitConverter.ToUInt64(values, start + i * 8);
|
||||||
|
}
|
||||||
|
return tULongs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool VerifySize(byte[] source, int headerSize)
|
||||||
|
{
|
||||||
|
if (source.Length < headerSize)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static byte[] ReadBytes(Stream s, int count)
|
||||||
|
{
|
||||||
|
byte[] bytes = new byte[count];
|
||||||
|
s.Read(bytes, 0, count);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enum Classifications
|
||||||
|
{
|
||||||
|
Created,
|
||||||
|
Unclassified,
|
||||||
|
Ground,
|
||||||
|
Vegetation_Low,
|
||||||
|
Vegetation_Med,
|
||||||
|
Vegetation_High,
|
||||||
|
Building,
|
||||||
|
LowPoint,
|
||||||
|
r8,
|
||||||
|
Water,
|
||||||
|
Rail,
|
||||||
|
RoadSurface,
|
||||||
|
r12,
|
||||||
|
Wire_Guard,
|
||||||
|
Wire_Conductor,
|
||||||
|
TransmissionTower,
|
||||||
|
Wire_StructureConnector,
|
||||||
|
BridgeDeck,
|
||||||
|
HighNoise,
|
||||||
|
OverheadStructure,
|
||||||
|
IgnoredGround,
|
||||||
|
Snow,
|
||||||
|
TemporalExclusion,
|
||||||
|
r23,
|
||||||
|
r24,
|
||||||
|
r25,
|
||||||
|
r26,
|
||||||
|
r27,
|
||||||
|
r28,
|
||||||
|
r29,
|
||||||
|
r30,
|
||||||
|
r31,
|
||||||
|
r32,
|
||||||
|
r33,
|
||||||
|
r34,
|
||||||
|
r36,
|
||||||
|
r37,
|
||||||
|
r38,
|
||||||
|
r39,
|
||||||
|
r40,
|
||||||
|
r41,
|
||||||
|
r42,
|
||||||
|
r43,
|
||||||
|
r44,
|
||||||
|
r45,
|
||||||
|
r46,
|
||||||
|
r47,
|
||||||
|
r48,
|
||||||
|
r49,
|
||||||
|
r50,
|
||||||
|
r51,
|
||||||
|
r52,
|
||||||
|
r53,
|
||||||
|
r54,
|
||||||
|
r55,
|
||||||
|
r56,
|
||||||
|
r57,
|
||||||
|
r58,
|
||||||
|
r59,
|
||||||
|
r60,
|
||||||
|
r61,
|
||||||
|
r62,
|
||||||
|
r63,
|
||||||
|
u64,
|
||||||
|
u65,
|
||||||
|
u66,
|
||||||
|
u67,
|
||||||
|
u68,
|
||||||
|
u69,
|
||||||
|
u70
|
||||||
|
}
|
||||||
|
}
|
406
LASRead/LASFormat/FileHeader.cs
Normal file
406
LASRead/LASFormat/FileHeader.cs
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Printing.IndexedProperties;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows.Markup;
|
||||||
|
|
||||||
|
namespace LASRead.LASFormat
|
||||||
|
{
|
||||||
|
class LASFormatDetail
|
||||||
|
{
|
||||||
|
/* Primative Types:
|
||||||
|
* (LAS = C#)
|
||||||
|
* char = byte
|
||||||
|
* uchar = char
|
||||||
|
* short = int16
|
||||||
|
* ushort = uint16
|
||||||
|
* long = int
|
||||||
|
* ulong = uint
|
||||||
|
* long long = int64
|
||||||
|
* ulong long = uint64
|
||||||
|
* float = float
|
||||||
|
* double = double
|
||||||
|
* string = 1-byte, ASCII chars, null terminated unless at max length (therefore no termination)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Public header:
|
||||||
|
* char[4] - signature
|
||||||
|
* ushort - file id
|
||||||
|
* ushort encoding
|
||||||
|
* ulong ?- guid 1
|
||||||
|
* ushort ?- guid 2
|
||||||
|
* ushort ?- guid 3
|
||||||
|
* uchar[8] ?- guid 4
|
||||||
|
* uchar
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
public class FileHeader
|
||||||
|
{
|
||||||
|
ushort headerSize;
|
||||||
|
// Follows the correct order of the header
|
||||||
|
char[] fileSignature;
|
||||||
|
ushort fileSourceID;
|
||||||
|
ushort globalEncoding;
|
||||||
|
// Optional Fields are filled with zero
|
||||||
|
uint GUIDData1 = 0;
|
||||||
|
ushort GUIDData2 = 0;
|
||||||
|
ushort GUIDData3 = 0;
|
||||||
|
byte[] GUIDData4 = new byte[] {0,0,0,0,0,0,0,0};
|
||||||
|
byte versionMajor;
|
||||||
|
byte versionMinor;
|
||||||
|
char[] systemIdentifier;
|
||||||
|
char[] generatingSoftware;
|
||||||
|
ushort fileCreationDayOfYear;
|
||||||
|
ushort fileCreationYear;
|
||||||
|
uint dataOffset;
|
||||||
|
uint numberVLRs; // Variable Length Records
|
||||||
|
byte pointDataRecordFormat;
|
||||||
|
ushort pointDataRecordLength;
|
||||||
|
uint legacyNumberOfPointRecords;
|
||||||
|
uint[] legacyNumberOfPointByReturn;
|
||||||
|
double x_scaleFactor;
|
||||||
|
double y_scaleFactor;
|
||||||
|
double z_scaleFactor;
|
||||||
|
double x_offset;
|
||||||
|
double y_offset;
|
||||||
|
double z_offset;
|
||||||
|
double x_max;
|
||||||
|
double x_min;
|
||||||
|
double y_max;
|
||||||
|
double y_min;
|
||||||
|
double z_max;
|
||||||
|
double z_min;
|
||||||
|
ulong startOfWaveformDPR; // Data Packet Record
|
||||||
|
ulong startOfFirstExtendedVLR;
|
||||||
|
uint numberOfExtendedVLRs;
|
||||||
|
ulong numberPointRecords;
|
||||||
|
ulong[] numberPointsByReturn;
|
||||||
|
/// <summary>
|
||||||
|
/// Signature of the data. Should always be "LASF"
|
||||||
|
/// </summary>
|
||||||
|
public char[] FileSignature { get => fileSignature; set => fileSignature = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// ID Associated with the data. Intended to differentiate between different sources
|
||||||
|
/// </summary>
|
||||||
|
public ushort FileSourceID { get => fileSourceID; set => fileSourceID = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// Flags Indicating the formats of certain data <br />
|
||||||
|
/// 0 : GPS Time Format (0 - Week time; 1 - standard GPS Time) <br />
|
||||||
|
/// 1 : Waveforms included (depricated) <br />
|
||||||
|
/// 2 : Waveforms in associated .wdp file (mutex with 1:) <br />
|
||||||
|
/// 3 : Point returns are synthetic <br />
|
||||||
|
/// 4 : Coordinate Reference System is WKT, else GeoTIFF <br />
|
||||||
|
/// 5-15 : Reserved <br />
|
||||||
|
/// </summary>
|
||||||
|
public ushort GlobalEncoding { get => globalEncoding; set => globalEncoding = value; }
|
||||||
|
public uint GUIDData11 { get => GUIDData1; set => GUIDData1 = value; }
|
||||||
|
public ushort GUIDData21 { get => GUIDData2; set => GUIDData2 = value; }
|
||||||
|
public ushort GUIDData31 { get => GUIDData3; set => GUIDData3 = value; }
|
||||||
|
public byte[] GUIDData41 { get => GUIDData4; set => GUIDData4 = value; }
|
||||||
|
public byte VersionMajor { get => versionMajor; set => versionMajor = value; }
|
||||||
|
public byte VersionMinor { get => versionMinor; set => versionMinor = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// Identifies the hardware used to collect the data <br />
|
||||||
|
/// Can be MERGE, MODIFICATION, EXTRACTION, TRANSFORMATION, OTHER, or a custom expression
|
||||||
|
/// </summary>
|
||||||
|
public char[] SystemIdentifier { get => systemIdentifier; set => systemIdentifier = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// Identifies the software used to encode the data
|
||||||
|
/// </summary>
|
||||||
|
public char[] GeneratingSoftware { get => generatingSoftware; set => generatingSoftware = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// The day of the year this file was created
|
||||||
|
/// </summary>
|
||||||
|
public ushort FileCreationDayOfYear { get => fileCreationDayOfYear; set => fileCreationDayOfYear = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// The year this data was collected
|
||||||
|
/// </summary>
|
||||||
|
public ushort FileCreationYear { get => fileCreationYear; set => fileCreationYear = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// The size of this header
|
||||||
|
/// </summary>
|
||||||
|
public ushort HeaderSize { get => headerSize; set => headerSize = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// The offset of the first Point-Data-Record (PDR)
|
||||||
|
/// </summary>
|
||||||
|
public uint DataOffset { get => dataOffset; set => dataOffset = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// The number of Variable Length Records (VLRs) present
|
||||||
|
/// </summary>
|
||||||
|
public uint NumberVLRs { get => numberVLRs; set => numberVLRs = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// An integer representing the format of the PDR data. <br />
|
||||||
|
/// See <see cref="IPointDataRecord"/>
|
||||||
|
/// </summary>
|
||||||
|
public byte PointDataRecordFormat { get => pointDataRecordFormat; set => pointDataRecordFormat = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// The length of a PDR record
|
||||||
|
/// </summary>
|
||||||
|
public ushort PointDataRecordLength { get => pointDataRecordLength; set => pointDataRecordLength = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// Number of point records for legacy compatibility (32 bits)
|
||||||
|
/// </summary>
|
||||||
|
public uint LegacyNumberOfPointRecords { get => legacyNumberOfPointRecords; set => legacyNumberOfPointRecords = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// todo
|
||||||
|
/// </summary>
|
||||||
|
public uint[] LegacyNumberOfPointByReturn { get => legacyNumberOfPointByReturn; set => legacyNumberOfPointByReturn = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// Scale factor of the X axis. Multiply by this value to get the true value
|
||||||
|
/// </summary>
|
||||||
|
public double X_scaleFactor { get => x_scaleFactor; set => x_scaleFactor = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// Scale factor of the Y axis. Multiply by this value to get the true value
|
||||||
|
/// </summary>
|
||||||
|
public double Y_scaleFactor { get => y_scaleFactor; set => y_scaleFactor = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// Scale factor of the Z axis. Multiply by this value to get the true value
|
||||||
|
/// </summary>
|
||||||
|
public double Z_scaleFactor { get => z_scaleFactor; set => z_scaleFactor = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// Offset of the X axis. <br />
|
||||||
|
/// Calculate Coordinates like so: <br />
|
||||||
|
/// X_final = X * X_scaleFactor + X_offset
|
||||||
|
/// </summary>
|
||||||
|
public double X_offset { get => x_offset; set => x_offset = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// Offset of the Y axis. <br />
|
||||||
|
/// Calculate Coordinates like so: <br />
|
||||||
|
/// Y_final = Y * Y_scaleFactor + Y_offset
|
||||||
|
/// </summary>
|
||||||
|
public double Y_offset { get => y_offset; set => y_offset = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// Offset of the Z axis. <br />
|
||||||
|
/// Calculate Coordinates like so: <br />
|
||||||
|
/// Z_final = Z * Z_scaleFactor + Z_offset
|
||||||
|
/// </summary>
|
||||||
|
public double Z_offset { get => z_offset; set => z_offset = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// Largest X value in the data
|
||||||
|
/// </summary>
|
||||||
|
public double X_max { get => x_max; set => x_max = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// Smallest X value in the data
|
||||||
|
/// </summary>
|
||||||
|
public double X_min { get => x_min; set => x_min = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// Largest Y value in the data
|
||||||
|
/// </summary>
|
||||||
|
public double Y_max { get => y_max; set => y_max = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// Smallest Y value in the data
|
||||||
|
/// </summary>
|
||||||
|
public double Y_min { get => y_min; set => y_min = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// Largest Z value in the data
|
||||||
|
/// </summary>
|
||||||
|
public double Z_max { get => z_max; set => z_max = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// Smallest Z value in the data
|
||||||
|
/// </summary>
|
||||||
|
public double Z_min { get => z_min; set => z_min = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// <b>New in 1.4</b><br />
|
||||||
|
/// Start of the Waveform Data Packet Records
|
||||||
|
/// </summary>
|
||||||
|
public ulong StartOfWaveformDPR { get => startOfWaveformDPR; set => startOfWaveformDPR = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// <b>New in 1.4</b><br />
|
||||||
|
/// Start of the first Extended Variable Length Record (EVLR)
|
||||||
|
/// </summary>
|
||||||
|
public ulong StartOfFirstExtendedVLR { get => startOfFirstExtendedVLR; set => startOfFirstExtendedVLR = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// <b>New in 1.4</b><br />
|
||||||
|
/// Number of Extended Variable Length Records
|
||||||
|
/// </summary>
|
||||||
|
public uint NumberOfExtendedVLRs { get => numberOfExtendedVLRs; set => numberOfExtendedVLRs = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// <b>New in 1.4</b><br />
|
||||||
|
/// Number of point records (64 bits)
|
||||||
|
/// </summary>
|
||||||
|
public ulong NumberPointRecords { get => numberPointRecords; set => numberPointRecords = value; }
|
||||||
|
/// <summary>
|
||||||
|
/// <b>New in 1.4</b><br />
|
||||||
|
/// todo
|
||||||
|
/// </summary>
|
||||||
|
public ulong[] NumberPointsByReturn { get => numberPointsByReturn; set => numberPointsByReturn = value; }
|
||||||
|
|
||||||
|
public byte[] GetAsByteArray()
|
||||||
|
{
|
||||||
|
byte[] endBytes = new byte[headerSize];
|
||||||
|
DataHelpers.ToByteArray(fileSignature).CopyTo(endBytes,0); //4
|
||||||
|
BitConverter.GetBytes(fileSourceID).CopyTo(endBytes, 4); // 2
|
||||||
|
BitConverter.GetBytes(globalEncoding).CopyTo(endBytes, 6); // 2
|
||||||
|
BitConverter.GetBytes(GUIDData1).CopyTo(endBytes, 8); // 4
|
||||||
|
BitConverter.GetBytes(GUIDData2).CopyTo(endBytes, 12); // 2
|
||||||
|
BitConverter.GetBytes(GUIDData3).CopyTo(endBytes, 14); // 2
|
||||||
|
GUIDData4.CopyTo(endBytes, 16); // 8
|
||||||
|
endBytes[24] = versionMajor;
|
||||||
|
endBytes[25] = versionMinor;
|
||||||
|
DataHelpers.ToByteArray(systemIdentifier).CopyTo(endBytes, 26); // 32 Bytes
|
||||||
|
DataHelpers.ToByteArray(GeneratingSoftware).CopyTo(endBytes, 58); // 32 Bytes
|
||||||
|
BitConverter.GetBytes(fileCreationDayOfYear).CopyTo(endBytes, 90); // 2
|
||||||
|
BitConverter.GetBytes(fileCreationYear).CopyTo(endBytes, 92); // 2
|
||||||
|
BitConverter.GetBytes(headerSize).CopyTo(endBytes, 94); // 2
|
||||||
|
BitConverter.GetBytes(dataOffset).CopyTo(endBytes, 96); // 4
|
||||||
|
BitConverter.GetBytes(numberVLRs).CopyTo(endBytes, 100); // 4
|
||||||
|
endBytes[104] = pointDataRecordFormat;
|
||||||
|
BitConverter.GetBytes(pointDataRecordLength).CopyTo(endBytes, 105); // 2
|
||||||
|
BitConverter.GetBytes(legacyNumberOfPointRecords).CopyTo(endBytes, 107); // 4
|
||||||
|
DataHelpers.ToByteArray(LegacyNumberOfPointByReturn).CopyTo(endBytes, 111); // 20 bytes
|
||||||
|
BitConverter.GetBytes(X_scaleFactor).CopyTo(endBytes, 131); // 8
|
||||||
|
BitConverter.GetBytes(Y_scaleFactor).CopyTo(endBytes, 139); // 8
|
||||||
|
BitConverter.GetBytes(Z_scaleFactor).CopyTo(endBytes, 147); // 8
|
||||||
|
BitConverter.GetBytes(X_offset).CopyTo(endBytes, 155); // 8
|
||||||
|
BitConverter.GetBytes(Y_offset).CopyTo(endBytes, 163); // 8
|
||||||
|
BitConverter.GetBytes(Z_offset).CopyTo(endBytes, 171); // 8
|
||||||
|
BitConverter.GetBytes(X_max).CopyTo(endBytes, 179); // 8
|
||||||
|
BitConverter.GetBytes(X_min).CopyTo(endBytes, 187); // 8
|
||||||
|
BitConverter.GetBytes(Y_max).CopyTo(endBytes, 195); // 8
|
||||||
|
BitConverter.GetBytes(Y_min).CopyTo(endBytes, 203); // 8
|
||||||
|
BitConverter.GetBytes(Z_max).CopyTo(endBytes, 211); // 8
|
||||||
|
BitConverter.GetBytes(Z_min).CopyTo(endBytes, 219); // 8
|
||||||
|
if (VersionMajor >= 1 && VersionMinor >= 4)
|
||||||
|
{
|
||||||
|
BitConverter.GetBytes(startOfWaveformDPR).CopyTo(endBytes, 227); // 8
|
||||||
|
BitConverter.GetBytes(StartOfFirstExtendedVLR).CopyTo(endBytes, 235); // 8
|
||||||
|
BitConverter.GetBytes(NumberOfExtendedVLRs).CopyTo(endBytes, 243); // 4
|
||||||
|
BitConverter.GetBytes(NumberPointRecords).CopyTo(endBytes, 247); // 8
|
||||||
|
DataHelpers.ToByteArray(NumberPointsByReturn).CopyTo(endBytes, 255); // 120 Bytes
|
||||||
|
}
|
||||||
|
return endBytes;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the header from the provided source
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source"></param>
|
||||||
|
/// <returns>The expected size of the header</returns>
|
||||||
|
public bool ReadHeader(Stream source)
|
||||||
|
{
|
||||||
|
source.Position = 94;
|
||||||
|
byte[] headerSizeBytes = new byte[2];
|
||||||
|
source.Read(headerSizeBytes, 0, 2);
|
||||||
|
headerSize = BitConverter.ToUInt16(headerSizeBytes, 0);
|
||||||
|
if (headerSize < 375)
|
||||||
|
{
|
||||||
|
// Assert the version is less than 1.4, where records after z-min were added.
|
||||||
|
source.Position = 24;
|
||||||
|
byte major = (byte)source.ReadByte();
|
||||||
|
byte minor = (byte)source.ReadByte();
|
||||||
|
if (major >= 1 && minor > 3)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException("Header is too small for data format >= v1.4");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
source.Position = 0;
|
||||||
|
|
||||||
|
byte[] inputHeader = new byte[headerSize];
|
||||||
|
source.Read(inputHeader, 0, HeaderSize);
|
||||||
|
|
||||||
|
FileSignature = new char[] { (char)inputHeader[0], (char)inputHeader[1], (char)inputHeader[2], (char)inputHeader[3] };
|
||||||
|
FileSourceID = BitConverter.ToUInt16(inputHeader, 4);
|
||||||
|
GlobalEncoding = BitConverter.ToUInt16(inputHeader, 6);
|
||||||
|
GUIDData11 = BitConverter.ToUInt32(inputHeader, 8);
|
||||||
|
GUIDData21 = BitConverter.ToUInt16(inputHeader, 12);
|
||||||
|
GUIDData31 = BitConverter.ToUInt16(inputHeader, 14);
|
||||||
|
GUIDData41 = new byte[] { inputHeader[16], inputHeader[17], inputHeader[18], inputHeader[19], inputHeader[20], inputHeader[21], inputHeader[22], inputHeader[23]};
|
||||||
|
VersionMajor = inputHeader[24];
|
||||||
|
VersionMinor = inputHeader[25];
|
||||||
|
SystemIdentifier = DataHelpers.ToCharArray(inputHeader, 26, 32);
|
||||||
|
GeneratingSoftware = DataHelpers.ToCharArray(inputHeader, 58, 32);
|
||||||
|
FileCreationDayOfYear = BitConverter.ToUInt16(inputHeader, 90);
|
||||||
|
FileCreationYear = BitConverter.ToUInt16(inputHeader, 92);
|
||||||
|
// 94 is headerSize
|
||||||
|
DataOffset = BitConverter.ToUInt32(inputHeader, 96);
|
||||||
|
NumberVLRs = BitConverter.ToUInt32(inputHeader, 100);
|
||||||
|
PointDataRecordFormat = inputHeader[104];
|
||||||
|
PointDataRecordLength = BitConverter.ToUInt16(inputHeader, 105);
|
||||||
|
LegacyNumberOfPointRecords = BitConverter.ToUInt32(inputHeader, 107);
|
||||||
|
LegacyNumberOfPointByReturn = DataHelpers.ToUintArray(inputHeader, 111, 20);
|
||||||
|
X_scaleFactor = BitConverter.ToDouble(inputHeader, 131);
|
||||||
|
Y_scaleFactor = BitConverter.ToDouble(inputHeader, 139);
|
||||||
|
Z_scaleFactor = BitConverter.ToDouble(inputHeader, 147);
|
||||||
|
X_offset = BitConverter.ToDouble(inputHeader, 155);
|
||||||
|
Y_offset = BitConverter.ToDouble(inputHeader, 163);
|
||||||
|
z_offset = BitConverter.ToDouble(inputHeader, 171);
|
||||||
|
X_max = BitConverter.ToDouble(inputHeader, 179);
|
||||||
|
X_min = BitConverter.ToDouble(inputHeader, 187);
|
||||||
|
Y_max = BitConverter.ToDouble(inputHeader, 195);
|
||||||
|
Y_min = BitConverter.ToDouble(inputHeader, 203);
|
||||||
|
Z_max = BitConverter.ToDouble(inputHeader, 211);
|
||||||
|
Z_min = BitConverter.ToDouble(inputHeader, 219);
|
||||||
|
if (versionMinor >= 4)
|
||||||
|
{
|
||||||
|
StartOfWaveformDPR = BitConverter.ToUInt64(inputHeader, 227);
|
||||||
|
StartOfFirstExtendedVLR = BitConverter.ToUInt64(inputHeader, 235);
|
||||||
|
NumberOfExtendedVLRs = BitConverter.ToUInt32(inputHeader, 243);
|
||||||
|
numberPointRecords = BitConverter.ToUInt64(inputHeader, 247);
|
||||||
|
NumberPointsByReturn = DataHelpers.ToULongArray(inputHeader, 255, 120);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append("Signature: " + new string(FileSignature) + Environment.NewLine);
|
||||||
|
sb.Append("SourceID: " + FileSourceID.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Global Encoding: " + GlobalEncoding.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("GUIDData1: " + GUIDData11.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("GUIDData2: " + GUIDData21.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("GUIDData3: " + GUIDData31.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("GUIDData4: ");
|
||||||
|
foreach (byte data in GUIDData41)
|
||||||
|
{
|
||||||
|
if (data != 0)
|
||||||
|
{
|
||||||
|
sb.Append((char)data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.Append(Environment.NewLine);
|
||||||
|
sb.Append("Version: " + VersionMajor.ToString() + "." + VersionMinor.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("System Identifier: "); sb.Append(SystemIdentifier); sb.Append(Environment.NewLine);
|
||||||
|
sb.Append("Generating Software: "); sb.Append(GeneratingSoftware); sb.Append(Environment.NewLine);
|
||||||
|
DateTime creationDate = new DateTime(FileCreationYear, 1, 1);
|
||||||
|
creationDate = creationDate.AddDays(FileCreationDayOfYear);
|
||||||
|
sb.Append("Creation Date: " + creationDate.ToShortDateString() + Environment.NewLine);
|
||||||
|
sb.Append("Header Length: " + HeaderSize.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Data Offset: " + DataOffset.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Number VRLs: " + NumberVLRs.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("PointDataFormat: " + PointDataRecordFormat.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("PointDataLength: " + PointDataRecordLength.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Legacy #PDRs: " + LegacyNumberOfPointRecords.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Legacy #PDR returns: ");
|
||||||
|
foreach (uint legacyReturn in LegacyNumberOfPointByReturn)
|
||||||
|
{
|
||||||
|
sb.Append(legacyReturn);
|
||||||
|
sb.Append(" ");
|
||||||
|
}
|
||||||
|
sb.Append(Environment.NewLine);
|
||||||
|
sb.Append("X Scale Factor: " + X_scaleFactor.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Y Scale Factor: " + Y_scaleFactor.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Z Scale Factor: " + Z_scaleFactor.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("X Offset: " + X_offset.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Y Offset: " + Y_offset.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Z Offset: " + Z_offset.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("X Max: " + X_max.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("X Min: " + X_min.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Y Max: " + Y_max.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Y Min: " + Y_min.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Z Max: " + Z_max.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Z Min: " + Z_min.ToString() + Environment.NewLine);
|
||||||
|
if (versionMinor >= 4)
|
||||||
|
{
|
||||||
|
sb.Append("Waveform Start: " + StartOfWaveformDPR.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Extended VLR Start: " + StartOfFirstExtendedVLR.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Number eVLRs: " + NumberOfExtendedVLRs.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Number Point Records: " + NumberPointRecords.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("NPR by Return: " + NumberPointsByReturn.ToString() + Environment.NewLine);
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
114
LASRead/LASFormat/PDRCollection.cs
Normal file
114
LASRead/LASFormat/PDRCollection.cs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
using LASRead.LASFormat;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace LASRead.LASFormat
|
||||||
|
{
|
||||||
|
public class PDRCollection<T> : IEnumerable where T : IPointDataRecord
|
||||||
|
{
|
||||||
|
readonly PDREnumerator<T> enumerator;
|
||||||
|
public PDRCollection(FileHeader fileHeader, Stream baseStream, IPointDataRecord initialRecord)
|
||||||
|
{
|
||||||
|
enumerator = new PDREnumerator<T>(fileHeader, baseStream, initialRecord);
|
||||||
|
}
|
||||||
|
public PDREnumerator<T> GetEnumerator()
|
||||||
|
{
|
||||||
|
return enumerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TEst()
|
||||||
|
{
|
||||||
|
return "sted";
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return enumerator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Enumerates through the points in a .las file
|
||||||
|
/// </summary>
|
||||||
|
public class PDREnumerator<T>: IEnumerator<T> where T : IPointDataRecord
|
||||||
|
{
|
||||||
|
readonly FileHeader baseHeader;
|
||||||
|
readonly Stream baseStream;
|
||||||
|
// readonly int headerSize;
|
||||||
|
//ulong endOfData;
|
||||||
|
ushort dataLength;
|
||||||
|
|
||||||
|
public PDREnumerator(FileHeader fileHeader, Stream baseStream, IPointDataRecord initialRecord)
|
||||||
|
{
|
||||||
|
Position = 0;
|
||||||
|
baseHeader = fileHeader;
|
||||||
|
this.baseStream = baseStream;
|
||||||
|
//endOfData = (fileHeader.StartOfFirstExtendedVLR == 0 ? (ulong)baseStream.Length : fileHeader.StartOfFirstExtendedVLR);
|
||||||
|
Current = (T)initialRecord;
|
||||||
|
dataLength = fileHeader.PointDataRecordLength;
|
||||||
|
|
||||||
|
}
|
||||||
|
// Stream which the data is sourced from
|
||||||
|
|
||||||
|
|
||||||
|
// Current payload
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type of the payload
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Type GetPDRFormat()
|
||||||
|
{
|
||||||
|
return Current.GetType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int EstimateRemainder()
|
||||||
|
{
|
||||||
|
return (int)((baseStream.Length - Position) / baseHeader.PointDataRecordLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
object IEnumerator.Current => Current;
|
||||||
|
|
||||||
|
public T Current { get; private set; }
|
||||||
|
|
||||||
|
public long Position { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (Position + dataLength >= baseStream.Length)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
long lastPos = baseStream.Position;
|
||||||
|
if (baseStream.Position != Position)
|
||||||
|
{
|
||||||
|
baseStream.Position = Position;
|
||||||
|
}
|
||||||
|
Position += dataLength;
|
||||||
|
IPointDataRecord newRecord = (IPointDataRecord)Activator.CreateInstance(Current.GetType());
|
||||||
|
byte[] nextData = new byte[dataLength];
|
||||||
|
baseStream.Position = Position;
|
||||||
|
baseStream.Read(nextData, 0, dataLength);
|
||||||
|
bool result = newRecord.ReadPoint(nextData);
|
||||||
|
Current = (T)newRecord;
|
||||||
|
baseStream.Position = lastPos;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
//throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
714
LASRead/LASFormat/PointDataRecord.cs
Normal file
714
LASRead/LASFormat/PointDataRecord.cs
Normal file
@ -0,0 +1,714 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace LASRead.LASFormat
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// LAS Data Payloads are in the Point Data Record (PDR) format <br />
|
||||||
|
/// PDR 0-5 share the same basic substructure (which is inherited from this interface, <br />
|
||||||
|
/// PDR 6-10 share a slightly different substructure (more flags)
|
||||||
|
/// </summary>
|
||||||
|
public interface IPointDataRecord
|
||||||
|
{
|
||||||
|
int X { get; set; }
|
||||||
|
int Y { get; set; }
|
||||||
|
int Z { get; set; }
|
||||||
|
|
||||||
|
Nullable<ushort> Intensity { get; set; }
|
||||||
|
byte ReturnNumberFlag_value { get; set; }
|
||||||
|
byte NumberOfReturnsFlag_value { get; set; }
|
||||||
|
byte ScanDirectionFlag_value { get; set; }
|
||||||
|
byte EdgeOfFlightLineFlag_value { get; set; }
|
||||||
|
byte Classification { get; set; }
|
||||||
|
sbyte ScanAngleRank { get; set; }
|
||||||
|
|
||||||
|
Nullable<byte> UserData { get; set; }
|
||||||
|
ushort PointSourceID { get; set; }
|
||||||
|
bool ReadPoint(byte[] data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the flags from the supplied byte
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool ReadFlag(Tuple<byte, byte> source);
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a new payload object from the supplied data bytes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Well-formed byte data (i.e. Read from file)</param>
|
||||||
|
/// <returns>A new payload object t</returns>
|
||||||
|
IPointDataRecord ParsePoint(byte[] data);
|
||||||
|
|
||||||
|
byte[] MergeFlags();
|
||||||
|
|
||||||
|
byte[] GetAsByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
class PDR0 : IPointDataRecord
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int z;
|
||||||
|
ushort? intensity;
|
||||||
|
byte returnNumberFlag_value;
|
||||||
|
byte numberOfReturnsFlag_value;
|
||||||
|
byte scanDirectionFlag_value;
|
||||||
|
byte edgeOfFlightLineFlag_value;
|
||||||
|
byte classification;
|
||||||
|
sbyte scanAngleRank;
|
||||||
|
byte? userData;
|
||||||
|
ushort pointSourceID;
|
||||||
|
|
||||||
|
public static readonly int headerSize = 20;
|
||||||
|
|
||||||
|
public int X { get => x; set => x = value; }
|
||||||
|
public int Y { get => y; set => y = value; }
|
||||||
|
public int Z { get => z; set => z = value; }
|
||||||
|
public ushort? Intensity { get => intensity; set => intensity = value; }
|
||||||
|
public byte ReturnNumberFlag_value { get => returnNumberFlag_value; set => returnNumberFlag_value = value; }
|
||||||
|
public byte NumberOfReturnsFlag_value { get => numberOfReturnsFlag_value; set => numberOfReturnsFlag_value = value; }
|
||||||
|
public byte ScanDirectionFlag_value { get => scanDirectionFlag_value; set => scanDirectionFlag_value = value; }
|
||||||
|
public byte EdgeOfFlightLineFlag_value { get => edgeOfFlightLineFlag_value; set => edgeOfFlightLineFlag_value = value; }
|
||||||
|
public byte Classification { get => classification; set => classification = value; }
|
||||||
|
public sbyte ScanAngleRank { get => scanAngleRank; set => scanAngleRank = value; }
|
||||||
|
public byte? UserData { get => userData; set => userData = value; }
|
||||||
|
public ushort PointSourceID { get => pointSourceID; set => pointSourceID = value; }
|
||||||
|
|
||||||
|
public virtual bool ReadFlag(Tuple<byte, byte> source)
|
||||||
|
{
|
||||||
|
// Note that typical Windows environments should be Little Endian; matching the expected data format.
|
||||||
|
// This means for the number 3,
|
||||||
|
// 7-6-5-4 3-2-1-0
|
||||||
|
// 0 0 0 0 0 0 1 1
|
||||||
|
// So return number flag = 2-1-0 (011) = 3
|
||||||
|
const byte full = 255;
|
||||||
|
ReturnNumberFlag_value = (byte)(source.Item1 & (full >> 5)); // Right-shift mask by 5 to get only the first 3 bits
|
||||||
|
NumberOfReturnsFlag_value = (byte)((source.Item1 >> 3) & (full >> 5)); // Right shift by 3, and & with 3 to get the returns
|
||||||
|
ScanDirectionFlag_value = (byte)((source.Item1 >> 6) & (full >> 7));
|
||||||
|
EdgeOfFlightLineFlag_value = (byte)(source.Item1 >> 7);
|
||||||
|
|
||||||
|
/* Big Endian
|
||||||
|
ReturnNumberFlag_value = (byte)((source.Item1 >> 5)& (full >> 5)); // Right-shift by 5 to get only the first 3 bits
|
||||||
|
NumberOfReturnsFlag_value = (byte)((source.Item1 >> 2) & (full >> 5)); // Right shift by 3, and & with 3 to get the returns
|
||||||
|
ScanDirectionFlag_value = (byte)((source.Item1 >> 1) & (full >> 7));
|
||||||
|
EdgeOfFlightLineFlag_value = (byte)(source.Item1 & (full >> 7)); */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool ReadPoint(byte[] data)
|
||||||
|
{
|
||||||
|
if (DataHelpers.VerifySize(data, headerSize))
|
||||||
|
{
|
||||||
|
x = BitConverter.ToInt32(data, 0);
|
||||||
|
y = BitConverter.ToInt32(data, 4);
|
||||||
|
z = BitConverter.ToInt32(data, 8);
|
||||||
|
intensity = BitConverter.ToUInt16(data, 12);
|
||||||
|
ReadFlag(Tuple.Create(data[14], (byte)0));
|
||||||
|
classification = data[15];
|
||||||
|
scanAngleRank = (sbyte)data[16];
|
||||||
|
userData = data[17];
|
||||||
|
pointSourceID = BitConverter.ToUInt16(data, 18);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool VerifySize(byte[] source, int headerSize)
|
||||||
|
{
|
||||||
|
if (source.Length < headerSize)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IPointDataRecord ParsePoint(byte[] data)
|
||||||
|
{
|
||||||
|
PDR0 newPoint = new PDR0();
|
||||||
|
newPoint.ReadPoint(data);
|
||||||
|
return newPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append(string.Format("Point: {0}, {1}, {2} {3}", X, Y, Z, Environment.NewLine));
|
||||||
|
sb.Append("Intensity: " + Intensity.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Return Number: " + ReturnNumberFlag_value.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Number of Returns: " + NumberOfReturnsFlag_value.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Scan Direction: " + (returnNumberFlag_value == 0 ? "+" : "-") + Environment.NewLine);
|
||||||
|
sb.Append("Edge of Flight Line: " + (returnNumberFlag_value == 0 ? "no" : "yes") + Environment.NewLine);
|
||||||
|
sb.Append("Classification: " + ((Classifications)Classification) + Environment.NewLine);
|
||||||
|
sb.Append("Scan Angle Rank: " + ScanAngleRank.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("User Data: " + (userData == 0 ? "no" : "yes") + Environment.NewLine);
|
||||||
|
sb.Append("Point Data Source: " + PointSourceID.ToString() + Environment.NewLine);
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] MergeFlags()
|
||||||
|
{
|
||||||
|
int result = EdgeOfFlightLineFlag_value;
|
||||||
|
result |= ReturnNumberFlag_value << 5; // Right-shift mask by 5 to get only the first 3 bits
|
||||||
|
result |= NumberOfReturnsFlag_value << 3; // Right shift by 3, and & with 3 to get the returns
|
||||||
|
result |= ScanDirectionFlag_value << 2;
|
||||||
|
byte t = (byte)(result & 255);
|
||||||
|
return new byte[] { t };
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual byte[] GetAsByteArray()
|
||||||
|
{
|
||||||
|
byte[] result = new byte[headerSize];
|
||||||
|
BitConverter.GetBytes(X).CopyTo(result, 0);
|
||||||
|
BitConverter.GetBytes(Y).CopyTo(result, 4);
|
||||||
|
BitConverter.GetBytes(Y).CopyTo(result, 8);
|
||||||
|
BitConverter.GetBytes(Intensity ?? 0).CopyTo(result, 12);
|
||||||
|
MergeFlags().CopyTo(result, 14);
|
||||||
|
result[15] = Classification;
|
||||||
|
result[16] = (byte)scanAngleRank;
|
||||||
|
result[17] = userData ?? 0;
|
||||||
|
BitConverter.GetBytes(PointSourceID).CopyTo(result, 18);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class PDR1 : PDR0
|
||||||
|
{
|
||||||
|
double GPSTime;
|
||||||
|
new public static readonly int headerSize = 28;
|
||||||
|
public override bool ReadPoint(byte[] data)
|
||||||
|
{
|
||||||
|
if (DataHelpers.VerifySize(data, headerSize))
|
||||||
|
{
|
||||||
|
base.ReadPoint(data);
|
||||||
|
GPSTime = BitConverter.ToDouble(data, 20);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
public override IPointDataRecord ParsePoint(byte[] data)
|
||||||
|
{
|
||||||
|
PDR1 newPoint = new PDR1();
|
||||||
|
newPoint.ReadPoint(data);
|
||||||
|
return newPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append(base.ToString());
|
||||||
|
sb.Append("GPS Time: " + GPSTime.ToString() + Environment.NewLine);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte[] GetAsByteArray()
|
||||||
|
{
|
||||||
|
byte[] result = new byte[headerSize];
|
||||||
|
base.GetAsByteArray().CopyTo(result, 0);
|
||||||
|
BitConverter.GetBytes(GPSTime).CopyTo(result, 20);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class PDR2 : PDR0
|
||||||
|
{
|
||||||
|
ushort red;
|
||||||
|
ushort green;
|
||||||
|
ushort blue;
|
||||||
|
new public static readonly int headerSize = 26;
|
||||||
|
public override bool ReadPoint(byte[] data)
|
||||||
|
{
|
||||||
|
if (DataHelpers.VerifySize(data, headerSize))
|
||||||
|
{
|
||||||
|
base.ReadPoint(data);
|
||||||
|
red = BitConverter.ToUInt16(data, 20);
|
||||||
|
green = BitConverter.ToUInt16(data, 22);
|
||||||
|
blue = BitConverter.ToUInt16(data, 24);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
public override IPointDataRecord ParsePoint(byte[] data)
|
||||||
|
{
|
||||||
|
PDR2 newPoint = new PDR2();
|
||||||
|
newPoint.ReadPoint(data);
|
||||||
|
return newPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append(base.ToString());
|
||||||
|
sb.Append(string.Format("RGB: {0} {1} {2} {3}", red, green, blue, Environment.NewLine));
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
public override byte[] GetAsByteArray()
|
||||||
|
{
|
||||||
|
byte[] result = new byte[headerSize];
|
||||||
|
base.GetAsByteArray().CopyTo(result, 0);
|
||||||
|
BitConverter.GetBytes(red).CopyTo(result, 20);
|
||||||
|
BitConverter.GetBytes(green).CopyTo(result, 22);
|
||||||
|
BitConverter.GetBytes(blue).CopyTo(result, 24);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class PDR3 : PDR1
|
||||||
|
{
|
||||||
|
ushort red;
|
||||||
|
ushort green;
|
||||||
|
ushort blue;
|
||||||
|
new public static readonly int headerSize = 34;
|
||||||
|
public override bool ReadPoint(byte[] data)
|
||||||
|
{
|
||||||
|
if (DataHelpers.VerifySize(data, headerSize))
|
||||||
|
{
|
||||||
|
base.ReadPoint(data);
|
||||||
|
red = BitConverter.ToUInt16(data, 28);
|
||||||
|
green = BitConverter.ToUInt16(data, 30);
|
||||||
|
blue = BitConverter.ToUInt16(data, 32);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
public override IPointDataRecord ParsePoint(byte[] data)
|
||||||
|
{
|
||||||
|
PDR3 newPoint = new PDR3();
|
||||||
|
newPoint.ReadPoint(data);
|
||||||
|
return newPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append(base.ToString());
|
||||||
|
sb.Append(string.Format("RGB: {0} {1} {2} {3}", red, green, blue, Environment.NewLine));
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
public override byte[] GetAsByteArray()
|
||||||
|
{
|
||||||
|
byte[] result = new byte[headerSize];
|
||||||
|
base.GetAsByteArray().CopyTo(result, 0);
|
||||||
|
BitConverter.GetBytes(red).CopyTo(result, 28);
|
||||||
|
BitConverter.GetBytes(green).CopyTo(result, 30);
|
||||||
|
BitConverter.GetBytes(blue).CopyTo(result, 32);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PDR4 : PDR1
|
||||||
|
{
|
||||||
|
byte wavePacketDescriptorIndex;
|
||||||
|
ulong WaveformOffset;
|
||||||
|
uint WaveformSize;
|
||||||
|
float returnPointWaveform;
|
||||||
|
float dx;
|
||||||
|
float dy;
|
||||||
|
float dz;
|
||||||
|
|
||||||
|
new public static readonly int headerSize = 57;
|
||||||
|
|
||||||
|
public override bool ReadPoint(byte[] data)
|
||||||
|
{
|
||||||
|
if (DataHelpers.VerifySize(data, headerSize))
|
||||||
|
{
|
||||||
|
base.ReadPoint(data);
|
||||||
|
wavePacketDescriptorIndex = data[28];
|
||||||
|
WaveformOffset = BitConverter.ToUInt64(data, 29);
|
||||||
|
WaveformSize = BitConverter.ToUInt32(data, 37);
|
||||||
|
returnPointWaveform = BitConverter.ToSingle(data, 41);
|
||||||
|
dx = BitConverter.ToSingle(data, 45);
|
||||||
|
dy = BitConverter.ToSingle(data, 49);
|
||||||
|
dz = BitConverter.ToSingle(data, 53);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
public override IPointDataRecord ParsePoint(byte[] data)
|
||||||
|
{
|
||||||
|
PDR4 newPoint = new PDR4();
|
||||||
|
newPoint.ReadPoint(data);
|
||||||
|
return newPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append(base.ToString());
|
||||||
|
sb.Append("WavePacket Index: " + wavePacketDescriptorIndex.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Waveform Offset: " + WaveformOffset.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Waveform Size: " + WaveformSize.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Return Point Waveform: " + returnPointWaveform.ToString() + Environment.NewLine);
|
||||||
|
sb.Append(string.Format("Delta Pos: dx={0} dy={1} dz={2} {3}", dx, dy, dz, Environment.NewLine));
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte[] GetAsByteArray()
|
||||||
|
{
|
||||||
|
byte[] result = new byte[headerSize];
|
||||||
|
base.GetAsByteArray().CopyTo(result, 0);
|
||||||
|
result[28] = wavePacketDescriptorIndex;
|
||||||
|
BitConverter.GetBytes(WaveformOffset).CopyTo(result, 29);
|
||||||
|
BitConverter.GetBytes(WaveformSize).CopyTo(result, 37);
|
||||||
|
BitConverter.GetBytes(returnPointWaveform).CopyTo(result, 41);
|
||||||
|
BitConverter.GetBytes(dx).CopyTo(result, 45);
|
||||||
|
BitConverter.GetBytes(dy).CopyTo(result, 49);
|
||||||
|
BitConverter.GetBytes(dz).CopyTo(result, 53);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class PDR5 : PDR3
|
||||||
|
{
|
||||||
|
byte wavePacketDescriptorIndex;
|
||||||
|
ulong WaveformOffset;
|
||||||
|
uint WaveformSize;
|
||||||
|
float returnPointWaveform;
|
||||||
|
float dx;
|
||||||
|
float dy;
|
||||||
|
float dz;
|
||||||
|
|
||||||
|
new public static readonly int headerSize = 63;
|
||||||
|
|
||||||
|
public override bool ReadPoint(byte[] data)
|
||||||
|
{
|
||||||
|
if (DataHelpers.VerifySize(data, headerSize))
|
||||||
|
{
|
||||||
|
base.ReadPoint(data);
|
||||||
|
wavePacketDescriptorIndex = data[34];
|
||||||
|
WaveformOffset = BitConverter.ToUInt64(data, 35);
|
||||||
|
WaveformSize = BitConverter.ToUInt32(data, 43);
|
||||||
|
returnPointWaveform = BitConverter.ToSingle(data, 47);
|
||||||
|
dx = BitConverter.ToSingle(data, 51);
|
||||||
|
dy = BitConverter.ToSingle(data, 55);
|
||||||
|
dz = BitConverter.ToSingle(data, 59);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
public static new IPointDataRecord ParsePoint(byte[] data)
|
||||||
|
{
|
||||||
|
PDR5 newPoint = new PDR5();
|
||||||
|
newPoint.ReadPoint(data);
|
||||||
|
return newPoint;
|
||||||
|
}
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append(base.ToString());
|
||||||
|
sb.Append("WavePacket Index: " + wavePacketDescriptorIndex.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Waveform Offset: " + WaveformOffset.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Waveform Size: " + WaveformSize.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Return Point Waveform: " + returnPointWaveform.ToString() + Environment.NewLine);
|
||||||
|
sb.Append(string.Format("Delta Pos: dx={0} dy={1} dz={2} {3}", dx, dy, dz, Environment.NewLine));
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
public override byte[] GetAsByteArray()
|
||||||
|
{
|
||||||
|
byte[] result = new byte[headerSize];
|
||||||
|
base.GetAsByteArray().CopyTo(result, 0);
|
||||||
|
result[34] = wavePacketDescriptorIndex;
|
||||||
|
BitConverter.GetBytes(WaveformOffset).CopyTo(result, 35);
|
||||||
|
BitConverter.GetBytes(WaveformSize).CopyTo(result, 43);
|
||||||
|
BitConverter.GetBytes(returnPointWaveform).CopyTo(result, 47);
|
||||||
|
BitConverter.GetBytes(dx).CopyTo(result, 51);
|
||||||
|
BitConverter.GetBytes(dy).CopyTo(result, 55);
|
||||||
|
BitConverter.GetBytes(dz).CopyTo(result, 59);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PDR6 : IPointDataRecord
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int z;
|
||||||
|
ushort? intensity;
|
||||||
|
byte returnNumberFlag_value; // NB: 4 bits here
|
||||||
|
byte numberOfReturnsFlag_value; // 4
|
||||||
|
byte classificationFlag_value; // 4
|
||||||
|
byte scannerChannelFlag_value; // 2
|
||||||
|
byte scanDirectionFlag_value; // 1
|
||||||
|
byte edgeOfFlightLineFlag_value; // 1
|
||||||
|
byte classification;
|
||||||
|
short scanAngleRank;
|
||||||
|
byte? userData;
|
||||||
|
ushort pointSourceID;
|
||||||
|
double GPSTime;
|
||||||
|
|
||||||
|
public static readonly int headerSize = 30;
|
||||||
|
// Inherited members
|
||||||
|
public int X { get => x; set => x = value; }
|
||||||
|
public int Y { get => y; set => y = value; }
|
||||||
|
public int Z { get => z; set => z = value; }
|
||||||
|
public ushort? Intensity { get => intensity; set => intensity = value; }
|
||||||
|
public byte ReturnNumberFlag_value { get => returnNumberFlag_value; set => returnNumberFlag_value = value; }
|
||||||
|
public byte NumberOfReturnsFlag_value { get => numberOfReturnsFlag_value; set => numberOfReturnsFlag_value = value; }
|
||||||
|
public byte ScanDirectionFlag_value { get => scanDirectionFlag_value; set => scanDirectionFlag_value = value; }
|
||||||
|
public byte EdgeOfFlightLineFlag_value { get => edgeOfFlightLineFlag_value; set => edgeOfFlightLineFlag_value = value; }
|
||||||
|
public byte Classification { get => classification; set => classification = value; }
|
||||||
|
public short ScanAngleRank { get => scanAngleRank; set => scanAngleRank = value; }
|
||||||
|
public byte? UserData { get => userData; set => userData = value; }
|
||||||
|
public ushort PointSourceID { get => pointSourceID; set => pointSourceID = value; }
|
||||||
|
// Local members
|
||||||
|
public byte ClassificationFlag_value { get => classificationFlag_value; set => classificationFlag_value = value; }
|
||||||
|
public byte ScannerChannelFlag_value { get => scannerChannelFlag_value; set => scannerChannelFlag_value = value; }
|
||||||
|
public double GPSTime1 { get => GPSTime; set => GPSTime = value; }
|
||||||
|
|
||||||
|
sbyte IPointDataRecord.ScanAngleRank { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||||
|
|
||||||
|
public bool ReadFlag(Tuple<byte, byte> source)
|
||||||
|
{
|
||||||
|
const byte full = 255;
|
||||||
|
|
||||||
|
ReturnNumberFlag_value = (byte)(source.Item1 & (full << 4));
|
||||||
|
NumberOfReturnsFlag_value = (byte)((source.Item1 >> 4) & (full << 4));
|
||||||
|
ClassificationFlag_value = (byte)(source.Item2 & (full << 4));
|
||||||
|
ScannerChannelFlag_value = (byte)((source.Item2 >> 4) & (full << 2));
|
||||||
|
ScanDirectionFlag_value = (byte)((source.Item2 >> 6) & (full << 1));
|
||||||
|
EdgeOfFlightLineFlag_value = (byte)(source.Item2 >> 7);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool ReadPoint(byte[] data)
|
||||||
|
{
|
||||||
|
if (DataHelpers.VerifySize(data, headerSize))
|
||||||
|
{
|
||||||
|
x = BitConverter.ToInt32(data, 0);
|
||||||
|
y = BitConverter.ToInt32(data, 4);
|
||||||
|
z = BitConverter.ToInt32(data, 8);
|
||||||
|
intensity = BitConverter.ToUInt16(data, 12);
|
||||||
|
ReadFlag(Tuple.Create(data[14], data[15]));
|
||||||
|
classification = data[16];
|
||||||
|
userData = data[17];
|
||||||
|
scanAngleRank = BitConverter.ToInt16(data, 18);
|
||||||
|
pointSourceID = BitConverter.ToUInt16(data, 20);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
public virtual IPointDataRecord ParsePoint(byte[] data)
|
||||||
|
{
|
||||||
|
PDR6 newPoint = new PDR6();
|
||||||
|
newPoint.ReadPoint(data);
|
||||||
|
return newPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append(string.Format("Point: {0}, {1}, {2} {3}", X, Y, Z, Environment.NewLine));
|
||||||
|
sb.Append("Intensity: " + Intensity.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Return Number: " + ReturnNumberFlag_value.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Number of Returns: " + NumberOfReturnsFlag_value.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Classification Value: " + ClassificationFlag_value + Environment.NewLine);
|
||||||
|
sb.Append("Scanner Channel Value: " + ScannerChannelFlag_value + Environment.NewLine);
|
||||||
|
sb.Append("Scan Direction: " + (returnNumberFlag_value == 0 ? "+" : "-") + Environment.NewLine);
|
||||||
|
sb.Append("Edge of Flight Line: " + (returnNumberFlag_value == 0 ? "no" : "yes") + Environment.NewLine);
|
||||||
|
sb.Append("Classification: " + ((Classifications)Classification) + Environment.NewLine);
|
||||||
|
sb.Append("Scan Angle Rank: " + ScanAngleRank.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("User Data: " + (userData == 0 ? "no" : "yes") + Environment.NewLine);
|
||||||
|
sb.Append("Point Data Source: " + PointSourceID.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("GPS Time: " + GPSTime1.ToString() + Environment.NewLine);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] MergeFlags()
|
||||||
|
{
|
||||||
|
int result = EdgeOfFlightLineFlag_value;
|
||||||
|
result |= ReturnNumberFlag_value << 5; // Right-shift mask by 5 to get only the first 3 bits
|
||||||
|
result |= NumberOfReturnsFlag_value << 3; // Right shift by 3, and & with 3 to get the returns
|
||||||
|
result |= ScanDirectionFlag_value << 2;
|
||||||
|
byte t = (byte)(result & 255);
|
||||||
|
throw new NotImplementedException();
|
||||||
|
return new byte[] { t };
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual byte[] GetAsByteArray()
|
||||||
|
{
|
||||||
|
byte[] result = new byte[headerSize];
|
||||||
|
BitConverter.GetBytes(X).CopyTo(result, 0);
|
||||||
|
BitConverter.GetBytes(Y).CopyTo(result, 4);
|
||||||
|
BitConverter.GetBytes(Y).CopyTo(result, 8);
|
||||||
|
BitConverter.GetBytes(Intensity ?? 0).CopyTo(result, 12);
|
||||||
|
MergeFlags().CopyTo(result, 14);
|
||||||
|
result[15] = Classification;
|
||||||
|
result[16] = (byte)scanAngleRank;
|
||||||
|
result[17] = userData ?? 0;
|
||||||
|
BitConverter.GetBytes(PointSourceID).CopyTo(result, 18);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PDR7 : PDR6
|
||||||
|
{
|
||||||
|
ushort red;
|
||||||
|
ushort green;
|
||||||
|
ushort blue;
|
||||||
|
|
||||||
|
new public static readonly int headerSize = 36;
|
||||||
|
|
||||||
|
public override bool ReadPoint(byte[] data)
|
||||||
|
{
|
||||||
|
if (DataHelpers.VerifySize(data, headerSize))
|
||||||
|
{
|
||||||
|
base.ReadPoint(data);
|
||||||
|
red = BitConverter.ToUInt16(data, 30);
|
||||||
|
green = BitConverter.ToUInt16(data, 32);
|
||||||
|
blue = BitConverter.ToUInt16(data, 34);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
public override IPointDataRecord ParsePoint(byte[] data)
|
||||||
|
{
|
||||||
|
PDR7 newPoint = new PDR7();
|
||||||
|
newPoint.ReadPoint(data);
|
||||||
|
return newPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append(base.ToString());
|
||||||
|
sb.Append(string.Format("RGB: {0} {1} {2} {3}", red, green, blue, Environment.NewLine));
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class PDR8 : PDR7
|
||||||
|
{
|
||||||
|
ushort nIR;
|
||||||
|
|
||||||
|
new public static readonly int headerSize = 38;
|
||||||
|
|
||||||
|
public override bool ReadPoint(byte[] data)
|
||||||
|
{
|
||||||
|
if (DataHelpers.VerifySize(data, headerSize))
|
||||||
|
{
|
||||||
|
base.ReadPoint(data);
|
||||||
|
nIR = BitConverter.ToUInt16(data, 36);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
public override IPointDataRecord ParsePoint(byte[] data)
|
||||||
|
{
|
||||||
|
PDR8 newPoint = new PDR8();
|
||||||
|
newPoint.ReadPoint(data);
|
||||||
|
return newPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append(base.ToString());
|
||||||
|
sb.Append("Infrared: " + nIR.ToString() + Environment.NewLine);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PDR9 : PDR6
|
||||||
|
{
|
||||||
|
byte wavePacketDescriptorIndex;
|
||||||
|
ulong byteOffsetToWaveformData;
|
||||||
|
uint waveformPacketSize;
|
||||||
|
float returnPointWaveformLocation;
|
||||||
|
float dx;
|
||||||
|
float dy;
|
||||||
|
float dz;
|
||||||
|
|
||||||
|
new public static readonly int headerSize = 59;
|
||||||
|
|
||||||
|
public override bool ReadPoint(byte[] data)
|
||||||
|
{
|
||||||
|
if (DataHelpers.VerifySize(data, headerSize))
|
||||||
|
{
|
||||||
|
base.ReadPoint(data);
|
||||||
|
wavePacketDescriptorIndex = data[38];
|
||||||
|
byteOffsetToWaveformData = BitConverter.ToUInt64(data, 39);
|
||||||
|
waveformPacketSize = BitConverter.ToUInt32(data, 47);
|
||||||
|
returnPointWaveformLocation = BitConverter.ToSingle(data, 51);
|
||||||
|
dx = BitConverter.ToSingle(data, 55);
|
||||||
|
dy = BitConverter.ToSingle(data, 59);
|
||||||
|
dz = BitConverter.ToSingle(data, 63);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
public override IPointDataRecord ParsePoint(byte[] data)
|
||||||
|
{
|
||||||
|
PDR9 newPoint = new PDR9();
|
||||||
|
newPoint.ReadPoint(data);
|
||||||
|
return newPoint;
|
||||||
|
}
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append(base.ToString());
|
||||||
|
sb.Append("WavePacket Index: " + wavePacketDescriptorIndex.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Waveform Offset: " + byteOffsetToWaveformData.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Waveform Size: " + waveformPacketSize.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Return Point Waveform: " + returnPointWaveformLocation.ToString() + Environment.NewLine);
|
||||||
|
sb.Append(string.Format("Delta Pos: dx={0} dy={1} dz={2} {3}", dx, dy, dz, Environment.NewLine));
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PDR10 : PDR8
|
||||||
|
{
|
||||||
|
byte wavePacketDescriptorIndex;
|
||||||
|
ulong byteOffsetToWaveformData;
|
||||||
|
uint waveformPacketSize;
|
||||||
|
float returnPointWaveformLocation;
|
||||||
|
float dx;
|
||||||
|
float dy;
|
||||||
|
float dz;
|
||||||
|
|
||||||
|
new public static readonly int headerSize = 67;
|
||||||
|
|
||||||
|
public override bool ReadPoint(byte[] data)
|
||||||
|
{
|
||||||
|
if (DataHelpers.VerifySize(data, headerSize))
|
||||||
|
{
|
||||||
|
base.ReadPoint(data);
|
||||||
|
wavePacketDescriptorIndex = data[30];
|
||||||
|
byteOffsetToWaveformData = BitConverter.ToUInt64(data, 31);
|
||||||
|
waveformPacketSize = BitConverter.ToUInt32(data, 39);
|
||||||
|
returnPointWaveformLocation = BitConverter.ToSingle(data, 43);
|
||||||
|
dx = BitConverter.ToSingle(data, 47);
|
||||||
|
dy = BitConverter.ToSingle(data, 51);
|
||||||
|
dz = BitConverter.ToSingle(data, 55);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
public override IPointDataRecord ParsePoint(byte[] data)
|
||||||
|
{
|
||||||
|
PDR10 newPoint = new PDR10();
|
||||||
|
newPoint.ReadPoint(data);
|
||||||
|
return newPoint;
|
||||||
|
}
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append(base.ToString());
|
||||||
|
sb.Append("WavePacket Index: " + wavePacketDescriptorIndex.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Waveform Offset: " + byteOffsetToWaveformData.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Waveform Size: " + waveformPacketSize.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Return Point Waveform: " + returnPointWaveformLocation.ToString() + Environment.NewLine);
|
||||||
|
sb.Append(string.Format("Delta Pos: dx={0} dy={1} dz={2} {3}", dx, dy, dz, Environment.NewLine));
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
205
LASRead/LASFormat/Record.cs
Normal file
205
LASRead/LASFormat/Record.cs
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
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;
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new Variable Length Record
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="header">The parsed header of the object</param>
|
||||||
|
/// <param name="position">The position of this header in the source stream</param>
|
||||||
|
public Record(IRecordPayloadHeader header, long position)
|
||||||
|
{
|
||||||
|
this.header = header;
|
||||||
|
data = new byte[header.RecordLengthAfterHeader];
|
||||||
|
this.position = position;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the data payload associated with this header
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="s">The source stream</param>
|
||||||
|
/// <returns>A bool representing success or failed</returns>
|
||||||
|
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));
|
||||||
|
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: " + RecordID.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Description: " + new string(DataHelpers.ToCharArray(Description)) + Environment.NewLine);
|
||||||
|
return base.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Extended Variable Length Records differ from VLRs in the maximum data that can be saved, and header size
|
||||||
|
/// </summary>
|
||||||
|
class EVLRHeader : VLRHeader
|
||||||
|
{
|
||||||
|
public new const int headerLength = 60;
|
||||||
|
|
||||||
|
new public ulong RecordLengthAfterHeader { get; set; }
|
||||||
|
new public int HeaderLength { get => 60; }
|
||||||
|
|
||||||
|
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: " + RecordID.ToString() + Environment.NewLine);
|
||||||
|
sb.Append("Description: " + new string(DataHelpers.ToCharArray(Description)) + Environment.NewLine);
|
||||||
|
return base.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
82
LASRead/LASFormat/RecordCollection.cs
Normal file
82
LASRead/LASFormat/RecordCollection.cs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace LASRead.LASFormat
|
||||||
|
{
|
||||||
|
public class RecordCollection : IEnumerable<Record>
|
||||||
|
{
|
||||||
|
RecordEnumerator enumerator;
|
||||||
|
public RecordCollection(Stream source, ulong startPosition, uint maxItems, IRecordPayloadHeader firstHeader)
|
||||||
|
{
|
||||||
|
enumerator = new RecordEnumerator(source, startPosition, maxItems, firstHeader);
|
||||||
|
}
|
||||||
|
public IEnumerator<Record> GetEnumerator()
|
||||||
|
{
|
||||||
|
return enumerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return enumerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public class RecordEnumerator : IEnumerator<Record>
|
||||||
|
{
|
||||||
|
Stream dataSource;
|
||||||
|
ulong streamStart;
|
||||||
|
ulong currentPosition;
|
||||||
|
uint currentCount;
|
||||||
|
uint maxCount;
|
||||||
|
|
||||||
|
public RecordEnumerator(Stream source, ulong startPosition, uint maxItems, IRecordPayloadHeader firstHeader)
|
||||||
|
{
|
||||||
|
dataSource = source;
|
||||||
|
streamStart = startPosition;
|
||||||
|
currentPosition = startPosition;
|
||||||
|
currentCount = 0;
|
||||||
|
maxCount = maxItems;
|
||||||
|
Current = new Record(firstHeader, (long)startPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
object IEnumerator.Current => Current;
|
||||||
|
|
||||||
|
public Record Current { get; private set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
dataSource = null;
|
||||||
|
streamStart = 0;
|
||||||
|
currentPosition = 0;
|
||||||
|
currentCount = 0;
|
||||||
|
maxCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (currentCount >= maxCount)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
Current = nextRecord;
|
||||||
|
dataSource.Position = oldPos;
|
||||||
|
currentCount++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
LASRead/LASRead.csproj
Normal file
9
LASRead/LASRead.csproj
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
<UseWPF>true</UseWPF>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
13
LASRead/MainWindow.xaml
Normal file
13
LASRead/MainWindow.xaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<Window x:Class="LASRead.MainWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:local="clr-namespace:LASRead"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="MainWindow" Height="450" Width="800">
|
||||||
|
<Grid>
|
||||||
|
<Button Content="Button" HorizontalAlignment="Left" Margin="165,138,0,0" VerticalAlignment="Top" Click="Button_Click"/>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
55
LASRead/MainWindow.xaml.cs
Normal file
55
LASRead/MainWindow.xaml.cs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Windows.Navigation;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
|
||||||
|
namespace LASRead
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for MainWindow.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class MainWindow : Window
|
||||||
|
{
|
||||||
|
public MainWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Button_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
// Configure open file dialog box
|
||||||
|
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
|
||||||
|
dlg.FileName = "Document"; // Default file name
|
||||||
|
dlg.DefaultExt = ".las"; // Default file extension
|
||||||
|
dlg.Filter = "Text documents (.las)|*.las"; // Filter files by extension
|
||||||
|
|
||||||
|
// Show open file dialog box
|
||||||
|
Nullable<bool> result = dlg.ShowDialog();
|
||||||
|
|
||||||
|
// Process open file dialog box results
|
||||||
|
if (result == true)
|
||||||
|
{
|
||||||
|
// Open document
|
||||||
|
string filename = dlg.FileName;
|
||||||
|
FileStream fs = File.OpenRead(filename);
|
||||||
|
byte[] readResults = new byte[375];
|
||||||
|
fs.Read(readResults, 0, 375);
|
||||||
|
//Header header = new Header();
|
||||||
|
//header.ReadHeader(readResults);
|
||||||
|
int i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
LASRead/RawPoints.cs
Normal file
59
LASRead/RawPoints.cs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
using LASFormat;
|
||||||
|
using LASRead.LASFormat;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace LASRead
|
||||||
|
{
|
||||||
|
public class RawPoints
|
||||||
|
{
|
||||||
|
// Creates a raw point cloud from a LAS file input.
|
||||||
|
LASFile source;
|
||||||
|
FileStream output;
|
||||||
|
|
||||||
|
public RawPoints(LASFile source, FileStream output)
|
||||||
|
{
|
||||||
|
this.source = source;
|
||||||
|
this.output = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
GenerateHeader();
|
||||||
|
byte[] tData = new byte[12];
|
||||||
|
foreach (IPointDataRecord pdr in source.points)
|
||||||
|
{
|
||||||
|
byte[] t = BitConverter.GetBytes(pdr.X);
|
||||||
|
t.CopyTo(tData, 0);
|
||||||
|
t = BitConverter.GetBytes(pdr.Y);
|
||||||
|
t.CopyTo(tData, 4);
|
||||||
|
t = BitConverter.GetBytes(pdr.Z);
|
||||||
|
t.CopyTo(tData, 8);
|
||||||
|
output.Write(tData);
|
||||||
|
}
|
||||||
|
output.Flush();
|
||||||
|
output.Close();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Header is 24 bytes (3 doubles)
|
||||||
|
/// </summary>
|
||||||
|
private void GenerateHeader()
|
||||||
|
{
|
||||||
|
byte[] finalBytes = new byte[24];
|
||||||
|
double x_scale = source.Header.X_scaleFactor;
|
||||||
|
double y_scale = source.Header.Y_scaleFactor;
|
||||||
|
double z_scale = source.Header.Z_scaleFactor;
|
||||||
|
|
||||||
|
byte[] t = BitConverter.GetBytes(x_scale);
|
||||||
|
t.CopyTo(finalBytes, 0);
|
||||||
|
t = BitConverter.GetBytes(y_scale);
|
||||||
|
t.CopyTo(finalBytes, 8);
|
||||||
|
t = BitConverter.GetBytes(z_scale);
|
||||||
|
t.CopyTo(finalBytes, 16);
|
||||||
|
|
||||||
|
output.Write(finalBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
PrintLasData/LasInteractor.csproj
Normal file
12
PrintLasData/LasInteractor.csproj
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\LASRead\LASRead.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
140
PrintLasData/Program.cs
Normal file
140
PrintLasData/Program.cs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using LASFormat;
|
||||||
|
using LASRead;
|
||||||
|
using LASRead.LASFormat;
|
||||||
|
using Microsoft.CSharp.RuntimeBinder;
|
||||||
|
|
||||||
|
namespace LasInteractor
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Console.WriteLine("LAS Interactor. Provides methods to interact with .las files.");
|
||||||
|
Console.WriteLine("(c) 2020 Brychan Dempsey.\n.las Format (c) 2002-2019 American Society for Photogrammetry and Remote Sensing (ASPRS)\n");
|
||||||
|
Console.WriteLine("This software is intended for testing purposes only.");
|
||||||
|
Console.WriteLine("Currently, it is only compatible with processors that work in Little-Endian byte orders. Your system is {0} LE.\n", BitConverter.IsLittleEndian ? "" : "not");
|
||||||
|
Console.WriteLine("Enter a mode:");
|
||||||
|
Console.WriteLine("r - read a .las file");
|
||||||
|
Console.WriteLine("g - generate a .las file");
|
||||||
|
Console.WriteLine("c - collects the points into xyz format");
|
||||||
|
Console.WriteLine("z - compress via 7zip (experimental)");
|
||||||
|
ConsoleKeyInfo c = Console.ReadKey();
|
||||||
|
while(c.Key != ConsoleKey.Q)
|
||||||
|
{
|
||||||
|
if (c.Key == ConsoleKey.R)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Read a file mode selected.\nPrint full data to console? (Y/n)");
|
||||||
|
c = Console.ReadKey();
|
||||||
|
while (c.Key != ConsoleKey.Y && c.Key != ConsoleKey.N)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Please enter Y/n");
|
||||||
|
c = Console.ReadKey();
|
||||||
|
}
|
||||||
|
Console.WriteLine("Enter the full file path:");
|
||||||
|
string path = Console.ReadLine();
|
||||||
|
ReadFile(path, c.Key == ConsoleKey.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
c = Console.ReadKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReadFile(string source, bool printAll)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Reading {0}", source);
|
||||||
|
FileStream fs = File.OpenRead(source);
|
||||||
|
Console.WriteLine("File is {0} bytes", fs.Length);
|
||||||
|
LASFile lasFile = new LASFile(fs);
|
||||||
|
/*FileStream os = File.OpenWrite("tdata.dat");
|
||||||
|
RawPoints rp = new RawPoints(lasFile, os);
|
||||||
|
rp.Run(); */
|
||||||
|
Console.WriteLine(new string('/', 20));
|
||||||
|
Console.Write(new string('/', 6));
|
||||||
|
Console.Write(" Header ");
|
||||||
|
Console.WriteLine(new string('/', 6));
|
||||||
|
Console.WriteLine(new string('/', 20));
|
||||||
|
Console.Write(lasFile.Header.ToString());
|
||||||
|
Console.WriteLine(new string('/', 20));
|
||||||
|
if (printAll)
|
||||||
|
{
|
||||||
|
Console.WriteLine("VLRs:");
|
||||||
|
Console.WriteLine("press enter to continue (q breaks the loop)");
|
||||||
|
Console.ReadLine();
|
||||||
|
foreach (Record record in lasFile.vlrCollection)
|
||||||
|
{
|
||||||
|
Console.WriteLine(record.ToString());
|
||||||
|
string s = Console.ReadLine();
|
||||||
|
if (s.Equals("q")) break;
|
||||||
|
}
|
||||||
|
Console.WriteLine("PDRs:");
|
||||||
|
Console.WriteLine("press enter to continue (q breaks the loop)");
|
||||||
|
Console.ReadLine();
|
||||||
|
byte lastClass = 0;
|
||||||
|
foreach (IPointDataRecord pdr in lasFile.points)
|
||||||
|
{
|
||||||
|
if (pdr.Classification == lastClass)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine(pdr.ToString());
|
||||||
|
lastClass = pdr.Classification;
|
||||||
|
// Generics abuse
|
||||||
|
// Searches points for the method GetEnumerator() (of PDRCollection<T>), then searches the returned PDREnumerator<T> for EstimateRemainder()
|
||||||
|
dynamic underlyingeEnumerator = lasFile.points.GetType().GetMethod("GetEnumerator").Invoke(lasFile.points, null);
|
||||||
|
string estimation = ((int)underlyingeEnumerator.GetType().GetMethod("EstimateRemainder").Invoke(underlyingeEnumerator, null)).ToString();
|
||||||
|
|
||||||
|
Console.WriteLine(estimation); //.GetEnumerator().EstimateRemainder());
|
||||||
|
string s = Console.ReadLine();
|
||||||
|
if (s.Equals("q")) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
Console.WriteLine("EVLRs:");
|
||||||
|
Console.WriteLine("press enter to continue (q breaks the loop)");
|
||||||
|
Console.ReadLine();
|
||||||
|
foreach (Record record in lasFile.evlrCollection)
|
||||||
|
{
|
||||||
|
Console.WriteLine(record.ToString());
|
||||||
|
string s = Console.ReadLine();
|
||||||
|
if (s.Equals("q")) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Number of VLRs:");
|
||||||
|
Console.WriteLine(lasFile.Header.NumberVLRs);
|
||||||
|
Console.WriteLine("Number of PDRs:");
|
||||||
|
Console.WriteLine(Math.Max(lasFile.Header.LegacyNumberOfPointRecords, lasFile.Header.NumberPointRecords));
|
||||||
|
if (lasFile.Header.VersionMajor >= 1 && lasFile.Header.VersionMinor >= 4)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Number of Extended VLRs:");
|
||||||
|
Console.WriteLine(lasFile.Header.NumberOfExtendedVLRs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("File Version is < v1.4. No additional data");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateLAS()
|
||||||
|
{
|
||||||
|
Stream s = new MemoryStream(); // Use a memory stream to save the enumerables. Copy the stream if needed
|
||||||
|
// Create a 163.84 * 163.84 * 163.84 grid
|
||||||
|
LASFile lasFile = new LASFile(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
README.md
Normal file
3
README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# LAS-Handler
|
||||||
|
Parses .las files into C# objects. These can then be interacted with, i.e. converted etc.
|
||||||
|
Also will allow creation of .las files from data
|
19
tests/Tests.csproj
Normal file
19
tests/Tests.csproj
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
|
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\LASRead\LASRead.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
23
tests/UnitTest1.cs
Normal file
23
tests/UnitTest1.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
using LASFormat;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Tests
|
||||||
|
{
|
||||||
|
public class Tests
|
||||||
|
{
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test1()
|
||||||
|
{
|
||||||
|
FileStream fs = File.OpenRead("C:\\points.las");
|
||||||
|
LASFile lasFile = new LASFile(fs);
|
||||||
|
|
||||||
|
Assert.Pass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user