3
// ts2las translates TerraSolid .bin file to ASPRS LAS file.
5
// TerraSolid format: http://cdn.terrasolid.fi/tscan.pdf
7
// (C) Copyright Howard Butler 2009, hobu.inc@gmail.com
9
// Distributed under the BSD License
10
// (See accompanying file LICENSE.txt or copy at
11
// http://www.opensource.org/licenses/bsd-license.php)
14
#include "laskernel.hpp"
19
#pragma warning(disable : 4512)
22
#include <boost/program_options.hpp>
28
namespace po = boost::program_options;
34
using namespace liblas;
36
// std::istream* OpenInput(std::string filename)
38
// std::ios::openmode const mode = std::ios::in | std::ios::binary;
39
// std::istream* istrm;
40
// if (compare_no_case(filename.c_str(),"STDIN",5) == 0)
46
// istrm = new std::ifstream(filename.c_str(), mode);
49
// if (!istrm->good())
52
// throw std::runtime_error("Reading stream was not able to be created");
58
// std::ostream* OpenOutput(std::string filename)
60
// std::ostream* ostrm;
61
// std::ios::openmode m;
62
// m = std::ios::out | std::ios::binary | std::ios::ate;
64
// if (compare_no_case(filename.c_str(),"STOUT",5) == 0)
66
// ostrm = &std::cout;
70
// ostrm = new std::ofstream(filename.c_str(), m);
74
// if (!ostrm->good())
77
// throw std::runtime_error("Writing stream was not able to be created");
84
liblas::Header CreateHeader(ScanHdr* hdr, bool verbose)
86
liblas::Header header;
88
// Checks for time and color values
89
liblas::PointFormatName format = liblas::ePointFormat0;
93
format = liblas::ePointFormat3;
95
format = liblas::ePointFormat1;
97
} else if (hdr->Color) {
98
format = liblas::ePointFormat2;
100
header.SetVersionMinor(2);
101
header.SetDataFormatId(format);
103
double scale = 1.0/(double)hdr->Units;
104
header.SetScale(scale, scale, scale);
105
header.SetOffset(hdr->OrgX*scale, hdr->OrgY*scale, hdr->OrgZ*scale);
106
header.SetPointRecordsCount(hdr->PntCnt);
110
std::cout << "The file says there should be " << hdr->PntCnt << " points" << std::endl;
111
std::cout << "units: " << hdr->Units << std::endl;
112
std::cout << "format: " << format << std::endl;
113
std::cout << "scale: " << scale << std::endl;
114
std::cout << "x origin: " << hdr->OrgX << std::endl;
115
std::cout << "y origin: " << hdr->OrgY << std::endl;
116
std::cout << "z origin: " << hdr->OrgZ << std::endl;
118
boost::uint32_t precision = GetStreamPrecision(scale);
120
std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
121
std::cout.precision(precision);
122
std::cout << "offset x: " << header.GetOffsetX() << std::endl;
123
std::cout << "offset y: " << header.GetOffsetY() << std::endl;
124
std::cout << "offset z: " << header.GetOffsetZ() << std::endl;
133
bool ReadHeader(ScanHdr* hdr, std::istream& istrm)
137
liblas::detail::read_n(*hdr, istrm, sizeof(ScanHdr));
139
if (hdr->Tunniste != 970401) return false;
140
if (memcmp(hdr->Magic,"CXYZ",4)) return false;
142
int version = hdr->HdrVersion;
143
if (version == 970404) return true;
144
if (version == 20010129) return true;
145
if (version == 20010712) return true;
146
if (version == 20020715) return true;
147
if (( version > 20020715) && (version < 20051231)) return true;
150
catch (std::exception const&)
157
bool WritePoints(liblas::Writer& writer, std::istream& strm, ScanHdr* hdr, bool verbose)
159
ScanPnt* point = new ScanPnt;
160
ScanRow* row = new ScanRow;
161
boost::uint32_t i = 0;
163
for (std::size_t t = 0; t < static_cast<std::size_t>(hdr->PntCnt); t++)
166
if (hdr->HdrVersion == 20020715) {
169
liblas::detail::read_n(*point, strm, sizeof(ScanPnt));
171
catch (std::out_of_range&) // we reached the end of the file
179
liblas::detail::read_n(*row, strm, sizeof(ScanRow));
181
catch (std::out_of_range&) // we reached the end of the file
187
point->Pnt.x = row->x;
188
point->Pnt.y = row->y;
189
point->Pnt.z = row->z;
190
point->Code = row->Code;
191
point->Line = row->Line;
192
point->Intensity = row->EchoInt & 0x3FFF;
193
point->Echo = (row->EchoInt >> 14);
197
p.SetRawX(point->Pnt.x);
198
p.SetRawY(point->Pnt.y);
199
p.SetRawZ(point->Pnt.z);
201
// std::cout << "read x: " << point->Pnt.x << " y: "<< point->Pnt.y << " z: " <<point->Pnt.z<< std::endl;
202
// std::cout << "wrote x: " << p.GetX() << " y: "<< p.GetY() << " z: " <<p.GetZ()<< std::endl;
203
// std::cout << "Code: " << point->Code << " Intensity: "<< point->Intensity << std::endl;
204
p.SetClassification(point->Code);
205
p.SetIntensity(point->Intensity);
207
boost::uint32_t t = 0xFFFFFFFF;
208
liblas::detail::read_n(t, strm, sizeof(t));
210
// Time stamps are assumed to be GPS week seconds. The
211
// storage format is a 32 bit unsigned integer where
212
// each integer step is 0.0002 seconds.
217
boost::uint8_t r, g, b, a = 0;
219
liblas::detail::read_n(r, strm, sizeof(r));
220
liblas::detail::read_n(b, strm, sizeof(b));
221
liblas::detail::read_n(g, strm, sizeof(g));
223
// TS .bin says to read 4 bytes here for some reason. Maybe
224
// this is an alpha value or something
225
liblas::detail::read_n(a, strm, sizeof(a));
235
TerraScan uses two bits for storing echo information. The possible values are:
241
if (point->Echo == 0) {
242
p.SetNumberOfReturns(1);
243
p.SetReturnNumber(1);
244
} else if (point->Echo == 1) {
245
p.SetReturnNumber(1);
246
} else if (point->Echo == 3) {
247
p.SetReturnNumber(2);
248
p.SetNumberOfReturns(2);
250
// I don't know what the hell to do here without cumulating
251
// through all of the points. Why wouldn't you store the return
253
p.SetReturnNumber(2);
254
p.SetNumberOfReturns(3);
258
writer.WritePoint(p);
259
} catch (std::exception)
261
std::cerr << "Point writing failed!" << std::endl;
265
term_progress(std::cout, (i + 1) / static_cast<double>(hdr->PntCnt));
275
void OutputHelp( std::ostream & oss, po::options_description const& options)
277
oss << "--------------------------------------------------------------------\n";
278
oss << " ts2las (" << GetFullVersion() << ")\n";
279
oss << "--------------------------------------------------------------------\n";
283
oss <<"\nFor more information, see the full documentation for ts2las at:\n";
285
oss << " http://liblas.org/utilities/ts2las.html\n";
286
oss << "----------------------------------------------------------\n";
290
int main(int argc, char* argv[])
294
bool verbose = false;
295
std::vector<liblas::FilterPtr> filters;
297
po::options_description ts2las_options("ts2las options");
298
po::options_description filtering_options = GetFilteringOptions();
299
po::positional_options_description p;
303
ts2las_options.add_options()
304
("help,h", "Produce this help message")
305
("input,i", po::value< std::string >(&input), "input TerraSolid .bin file")
306
("output,o", po::value< std::string >(&output)->default_value(""), "The output .las file (defaults to input filename + .las)")
307
("verbose,v", po::value<bool>(&verbose)->zero_tokens(), "Verbose message output")
311
po::variables_map vm;
312
po::options_description options;
313
options.add(ts2las_options).add(filtering_options);
314
po::store(po::command_line_parser(argc, argv).
315
options(options).positional(p).run(), vm);
320
if (vm.count("help"))
322
OutputHelp(std::cout, options);
328
std::cerr << "No input TerraSolid .bin file was specfied!" << std::endl;
329
OutputHelp(std::cout, options);
334
filters = GetFilters(vm, verbose);
337
std::cout << "input: " << input<< " output: " <<output<<std::endl;
339
ScanHdr* hdr = new ScanHdr;
342
bool opened = liblas::Open(istrm, input);
345
std::cerr << "Could not open file '" << input << "' to read TerraSolid .bin data! " << std::endl;
350
bool created = liblas::Create(ostrm, output);
353
std::cerr << "Could not create file " << output << " to write LAS data!" << std::endl;;
358
success = ReadHeader(hdr, istrm);
361
std::cerr<<"Unable to read " << input << "to read file!" << std::endl;
365
// std::cout << "stream position is: " << istrm->tellg() << std::endl;
366
liblas::Header header = CreateHeader(hdr, verbose);
367
liblas::Writer writer(ostrm, header);
369
writer.SetFilters(filters);
371
success = WritePoints(writer, istrm, hdr, verbose);
375
std::cout << "Successfully wrote " << header.GetPointRecordsCount()
376
<<" points to " << output << std::endl;