1 /******************************************************************************
2 * $Id$
3 *
4 * Project: libLAS - http://liblas.org - A BSD library for LAS format data.
5 * Purpose: A grab bucket 'o fun for C++ libLAS
6 * Author: Mateusz Loskot, mateusz@loskot.net
7 *
8 ******************************************************************************
9 * Copyright (c) 2008, Mateusz Loskot
10 *
11 * All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following
15 * conditions are met:
16 *
17 * * Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * * Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in
21 * the documentation and/or other materials provided
22 * with the distribution.
23 * * Neither the name of the Martin Isenburg or Iowa Department
24 * of Natural Resources nor the names of its contributors may be
25 * used to endorse or promote products derived from this software
26 * without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
31 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
32 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
33 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
34 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
35 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
36 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
37 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
38 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
39 * OF SUCH DAMAGE.
40 ****************************************************************************/
41
42 #ifndef LIBLAS_DETAIL_UTILITY_HPP_INCLUDED
43 #define LIBLAS_DETAIL_UTILITY_HPP_INCLUDED
44
45 #include <liblas/detail/pointrecord.hpp>
46 #include <liblas/detail/endian.hpp>
47 #include <liblas/detail/binary.hpp>
48 // boost
49 #include <boost/concept_check.hpp>
50 // std
51 #include <cassert>
52 #include <cstddef>
53 #include <cstring>
54 #include <algorithm>
55 #include <iosfwd>
56 #include <limits>
57 #include <sstream>
58 #include <stdexcept>
59 #include <cmath>
60 #include <vector>
61
62 /// Defines utilities for internal use in libLAS.
63 /// The liblas::detail elements do not belong to the public
64 /// interface of libLAS.
65 namespace liblas { namespace detail {
66
67 // From http://stackoverflow.com/questions/485525/round-for-float-in-c
sround(double r)68 inline double sround(double r) {
69 return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5);
70 }
71
72
73 /// Compile-time calculation size of array defined statically.
74 template <typename T, std::size_t N>
static_array_size(T (& t)[N])75 inline std::size_t static_array_size(T (&t)[N])
76 {
77 boost::ignore_unused_variable_warning(t);
78 return (sizeof(t) / sizeof(t[0]));
79 }
80
81 /// Simple RAII wrapper.
82 /// It's dedicated to use with types associated with custom deleter,
83 /// opaque pointers and C API objects.
84 template <typename T>
85 class raii_wrapper
86 {
87 typedef void(*deleter_type)(T* p);
88
89 public:
90
raii_wrapper(T * p,deleter_type d)91 raii_wrapper(T* p, deleter_type d)
92 : p_(p), del_(d)
93 {
94 assert(0 != p_);
95 assert(0 != del_);
96 }
97
operator =(raii_wrapper const & rhs)98 raii_wrapper& operator=(raii_wrapper const& rhs)
99 {
100 if (&rhs != this)
101 {
102 p_ = rhs.p_;
103 del_ = rhs.del_;
104 }
105 return *this;
106 }
107
108
~raii_wrapper()109 ~raii_wrapper()
110 {
111 do_delete(p_);
112 }
113
reset(T * p)114 void reset(T* p)
115 {
116 do_delete(p_);
117 p_= p;
118 }
119
get() const120 T* get() const
121 {
122 return p_;
123 }
124
swap(raii_wrapper & other)125 void swap(raii_wrapper& other)
126 {
127 std::swap(p_, other.p_);
128 }
129
130
131 private:
132
133 raii_wrapper(raii_wrapper const& other);
134 // raii_wrapper& operator=(raii_wrapper const& rhs);
135
do_delete(T * p)136 void do_delete(T* p)
137 {
138 assert(del_);
139 if (0 != p)
140 del_(p);
141 }
142
143 T* p_;
144 deleter_type del_;
145 };
146
147
148 /// Definition of variable-length record header.
149 struct VLRHeader
150 {
151 uint16_t reserved;
152 char userId[16]; // TODO: replace wtih boost::array --mloskot
153 uint16_t recordId;
154 uint16_t recordLengthAfterHeader;
155 char description[32]; // TODO: replace wtih boost::array --mloskot
156 };
157
158 template <typename T>
159 bool compare_distance(const T& actual, const T& expected);
160
161 template <typename T>
162 struct Point
163 {
Pointliblas::detail::Point164 Point()
165 : x(T()), y(T()), z(T())
166 {}
167
Pointliblas::detail::Point168 Point(T const& x, T const& y, T const& z)
169 : x(x), y(y), z(z)
170 {}
171
equalliblas::detail::Point172 bool equal(Point<T> const& other) const
173 {
174 return (compare_distance(x, other.x)
175 && compare_distance(y, other.y)
176 && compare_distance(z, other.z));
177 }
178
179 T x;
180 T y;
181 T z;
182 };
183
184 template <typename T>
operator ==(Point<T> const & lhs,Point<T> const & rhs)185 bool operator==(Point<T> const& lhs, Point<T> const& rhs)
186 {
187 return lhs.equal(rhs);
188 }
189
190 template <typename T>
operator !=(Point<T> const & lhs,Point<T> const & rhs)191 bool operator!=(Point<T> const& lhs, Point<T> const& rhs)
192 {
193 return (!lhs.equal(rhs));
194 }
195
196 // template <typename T>
197 // struct Extents
198 // {
199 // Extents() {}
200 // Extents(detail::Point<T> const& min, detail::Point<T> const& max)
201 // : min(min), max(max)
202 // {}
203 //
204 // bool equal(Extents<T> const& other) const
205 // {
206 // return (min == other.min && max == other.max);
207 // }
208 //
209 // typename detail::Point<T> min;
210 // typename detail::Point<T> max;
211 // };
212 //
213 // template <typename T>
214 // bool operator==(Extents<T> const& lhs, Extents<T> const& rhs)
215 // {
216 // return lhs.equal(rhs);
217 // }
218 //
219 // template <typename T>
220 // bool operator!=(Extents<T> const& lhs, Extents<T> const& rhs)
221 // {
222 // return (!lhs.equal(rhs));
223 // }
224
225 template <typename T>
compare_distance(const T & actual,const T & expected)226 bool compare_distance(const T& actual, const T& expected)
227 {
228 const T epsilon = std::numeric_limits<T>::epsilon();
229 const T diff = actual - expected;
230
231 if ( !((diff <= epsilon) && (diff >= -epsilon )) )
232 {
233 return false;
234 }
235
236 return true;
237 }
238
239 template<typename T>
as_buffer(T & data)240 inline char* as_buffer(T& data)
241 {
242 return static_cast<char*>(static_cast<void*>(&data));
243 }
244
245 template<typename T>
as_buffer(T * data)246 inline char* as_buffer(T* data)
247 {
248 return static_cast<char*>(static_cast<void*>(data));
249 }
250
251 template<typename T>
as_bytes(T const & data)252 inline char const* as_bytes(T const& data)
253 {
254 return static_cast<char const*>(static_cast<void const*>(&data));
255 }
256
257 template<typename T>
as_bytes(T const * data)258 inline char const* as_bytes(T const* data)
259 {
260 return static_cast<char const*>(static_cast<void const*>(data));
261 }
262
263 template <typename C, typename T>
check_stream_state(std::basic_ios<C,T> & srtm)264 inline void check_stream_state(std::basic_ios<C, T>& srtm)
265 {
266 // NOTE: Detailed stream check disabled in optimized
267 // builds to increase performance.
268 #if defined(DEBUG) || defined(_DEBUG)
269 // Test stream state bits
270 if (srtm.eof())
271 throw std::out_of_range("end of file encountered");
272 else if (srtm.fail())
273 throw std::runtime_error("non-fatal I/O error occured");
274 else if (srtm.bad())
275 throw std::runtime_error("fatal I/O error occured");
276 #else
277 boost::ignore_unused_variable_warning(srtm);
278 #endif
279 }
280
281 #ifdef _MSC_VER
282 # pragma warning(push)
283 # pragma warning(disable: 4127) // conditional expression is constant.
284 #endif
285
286 template <typename T>
read_n(T & dest,std::istream & src,std::streamsize const & num)287 inline void read_n(T& dest, std::istream& src, std::streamsize const& num)
288 {
289 // TODO: Review and redesign errors handling logic if necessary
290 if (!src)
291 throw std::runtime_error("detail::liblas::read_n<T> input stream is not readable");
292
293 src.read(detail::as_buffer(dest), num);
294
295 // Fix little-endian
296 LIBLAS_SWAP_BYTES_N(dest, num);
297
298 check_stream_state(src);
299 }
300
301 template <>
read_n(PointRecord & dest,std::istream & src,std::streamsize const & num)302 inline void read_n<PointRecord>(PointRecord& dest, std::istream& src, std::streamsize const& num)
303 {
304 // TODO: Review and redesign errors handling logic if necessary
305 if (!src)
306 throw std::runtime_error("detail::liblas::read_n<PointRecord> input stream is not readable");
307
308 src.read(detail::as_buffer(dest), num);
309 check_stream_state(src);
310
311 // Fix little-endian
312 LIBLAS_SWAP_BYTES(dest.x);
313 LIBLAS_SWAP_BYTES(dest.y);
314 LIBLAS_SWAP_BYTES(dest.z);
315 LIBLAS_SWAP_BYTES(dest.intensity);
316 LIBLAS_SWAP_BYTES(dest.flags);
317 LIBLAS_SWAP_BYTES(dest.classification);
318 LIBLAS_SWAP_BYTES(dest.scan_angle_rank);
319 LIBLAS_SWAP_BYTES(dest.user_data);
320 LIBLAS_SWAP_BYTES(dest.point_source_id);
321 }
322
323 template <>
read_n(VLRHeader & dest,std::istream & src,std::streamsize const & num)324 inline void read_n<VLRHeader>(VLRHeader& dest, std::istream& src, std::streamsize const& num)
325 {
326 // TODO: Review and redesign errors handling logic if necessary
327 if (!src)
328 throw std::runtime_error("detail::liblas::read_n<VLRHeader> input stream is not readable");
329
330 src.read(detail::as_buffer(dest), num);
331 check_stream_state(src);
332
333 // Fix little-endian
334 LIBLAS_SWAP_BYTES(dest.reserved);
335 LIBLAS_SWAP_BYTES(dest.recordId);
336 LIBLAS_SWAP_BYTES(dest.recordLengthAfterHeader);
337 }
338
339 template <>
read_n(std::string & dest,std::istream & src,std::streamsize const & num)340 inline void read_n<std::string>(std::string& dest, std::istream& src, std::streamsize const& num)
341 {
342 assert(dest.max_size() >= static_cast<std::string::size_type>(num));
343
344 // TODO: Review and redesign errors handling logic if necessary
345 if (!src)
346 throw std::runtime_error("detail::liblas::read_n<std::string> input stream is not readable");
347
348 // Read bytes into temporary buffer then assign as string
349 std::size_t const bufsize = static_cast<std::size_t>(num);
350 char* buf = new char[bufsize]; // TODO:this is a leak, make exception safe with RAII
351 src.read(buf, num);
352 dest.assign(buf, bufsize);
353 delete [] buf;
354
355 assert(dest.size() == static_cast<std::string::size_type>(num));
356 check_stream_state(src);
357 }
358
359 // adapted from http://www.cplusplus.com/forum/beginner/3076/
360 template <typename IntegerType>
bitsToInt(IntegerType & output,std::vector<uint8_t> const & data,std::size_t index)361 inline IntegerType bitsToInt(IntegerType& output,
362 std::vector<uint8_t> const& data,
363 std::size_t index)
364 {
365 binary::endian_value<IntegerType> value;
366 value.template load<binary::little_endian_tag>(&data[0] + index);
367 output = value;
368 return output;
369 }
370
371 template <typename IntegerType>
intToBits(IntegerType input,std::vector<uint8_t> & data,std::size_t index)372 inline void intToBits(IntegerType input,
373 std::vector<uint8_t>& data,
374 std::size_t index)
375 {
376 binary::endian_value<IntegerType> value(input);
377 value.template store<binary::little_endian_tag>(&data[0] + index);
378 }
379
380
381 template <typename T>
write_n(std::ostream & dest,T const & src,std::streamsize const & num)382 inline void write_n(std::ostream& dest, T const& src, std::streamsize const& num)
383 {
384 if (!dest)
385 throw std::runtime_error("detail::liblas::write_n<T>: output stream is not writable");
386
387 // Fix little-endian
388 T& tmp = const_cast<T&>(src);
389 LIBLAS_SWAP_BYTES_N(tmp, num);
390
391 dest.write(detail::as_bytes(tmp), num);
392 check_stream_state(dest);
393 }
394
395 template <>
write_n(std::ostream & dest,PointRecord const & src,std::streamsize const & num)396 inline void write_n<PointRecord>(std::ostream& dest, PointRecord const& src, std::streamsize const& num)
397 {
398 if (!dest)
399 throw std::runtime_error("detail::liblas::write_n<PointRecord>: output stream is not writable");
400
401 // Fix little-endian
402 PointRecord& tmp = const_cast<PointRecord&>(src);
403 LIBLAS_SWAP_BYTES(tmp.x);
404 LIBLAS_SWAP_BYTES(tmp.y);
405 LIBLAS_SWAP_BYTES(tmp.z);
406 LIBLAS_SWAP_BYTES(tmp.intensity);
407 LIBLAS_SWAP_BYTES(tmp.flags);
408 LIBLAS_SWAP_BYTES(tmp.classification);
409 LIBLAS_SWAP_BYTES(tmp.scan_angle_rank);
410 LIBLAS_SWAP_BYTES(tmp.user_data);
411 LIBLAS_SWAP_BYTES(tmp.point_source_id);
412
413 dest.write(detail::as_bytes(tmp), num);
414 check_stream_state(dest);
415 }
416
417 template <>
write_n(std::ostream & dest,VLRHeader const & src,std::streamsize const & num)418 inline void write_n<VLRHeader>(std::ostream& dest, VLRHeader const& src, std::streamsize const& num)
419 {
420 if (!dest)
421 throw std::runtime_error("detail::liblas::write_n<VLRHeader>: output stream is not writable");
422
423 // Fix little-endian
424 VLRHeader& tmp = const_cast<VLRHeader&>(src);
425 LIBLAS_SWAP_BYTES(tmp.reserved);
426 LIBLAS_SWAP_BYTES(tmp.recordId);
427 LIBLAS_SWAP_BYTES(tmp.recordLengthAfterHeader);
428
429 dest.write(detail::as_bytes(tmp), num);
430 check_stream_state(dest);
431 }
432
433 template <>
write_n(std::ostream & dest,std::string const & src,std::streamsize const & num)434 inline void write_n<std::string>(std::ostream& dest, std::string const& src, std::streamsize const& num)
435 {
436 if (!dest)
437 throw std::runtime_error("detail::liblas::write_n<std::string>: output stream is not writable");
438
439 dest.write(src.c_str(), num);
440 check_stream_state(dest);
441 }
442
443 #ifdef _MSC_VER
444 # pragma warning(pop)
445 #endif
446
447 // Utility functor with accompanying to print GeoTIFF directory.
448 struct geotiff_dir_printer
449 {
geotiff_dir_printerliblas::detail::geotiff_dir_printer450 geotiff_dir_printer() {}
451
outputliblas::detail::geotiff_dir_printer452 std::string output() const { return m_oss.str(); }
sizeliblas::detail::geotiff_dir_printer453 std::string::size_type size() const { return m_oss.str().size(); }
454
operator ()liblas::detail::geotiff_dir_printer455 void operator()(char* data, void* aux)
456 {
457 ::boost::ignore_unused_variable_warning(aux);
458
459 if (0 != data)
460 {
461 m_oss << data;
462 }
463 }
464
465 private:
466 std::ostringstream m_oss;
467 };
468
469 extern "C" int libLASGeoTIFFPrint(char* data, void* aux);
470
471 }} // namespace liblas::detail
472
473 #endif // LIBLAS_DETAIL_UTILITY_HPP_INCLUDED
474