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