1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  libLAS - http://liblas.org - A BSD library for LAS format data.
5  * Purpose:  LAS point class
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 #include <liblas/point.hpp>
43 #include <liblas/header.hpp>
44 #include <liblas/schema.hpp>
45 #include <liblas/exception.hpp>
46 #include <liblas/detail/binary.hpp>
47 #include <liblas/detail/pointrecord.hpp>
48 // boost
49 #include <boost/array.hpp>
50 #include <boost/cstdint.hpp>
51 #include <liblas/external/property_tree/ptree.hpp>
52 
53 // std
54 #include <cstring>
55 #include <stdexcept>
56 #include <string>
57 #include <vector>
58 #include <iosfwd>
59 #include <algorithm>
60 #include <numeric>
61 
62 using namespace boost;
63 
64 namespace liblas {
65 
Point(Header const * hdr)66 Point::Point(Header const* hdr)
67     :
68      m_header(hdr)
69     , m_default_header(DefaultHeader::get())
70 {
71     m_data.resize(hdr->GetDataRecordLength());
72     m_data.assign(hdr->GetDataRecordLength(), 0);
73 }
74 
Point(Point const & other)75 Point::Point(Point const& other)
76     : m_data(other.m_data)
77     , m_header(other.GetHeader())
78     , m_default_header(DefaultHeader::get())
79 {
80 }
81 
operator =(Point const & rhs)82 Point& Point::operator=(Point const& rhs)
83 {
84     if (&rhs != this)
85     {
86         m_data = rhs.m_data;
87         m_header = rhs.m_header;
88     }
89     return *this;
90 }
91 
SetCoordinates(double const & x,double const & y,double const & z)92 void Point::SetCoordinates(double const& x, double const& y, double const& z)
93 {
94     SetX(x);
95     SetY(y);
96     SetZ(z);
97 }
98 
equal(Point const & other) const99 bool Point::equal(Point const& other) const
100 {
101     // TODO - mloskot: Default epsilon is too small.
102     //                 Is 0.00001 good as tolerance or too wide?
103     //double const epsilon = std::numeric_limits<double>::epsilon();
104     double const epsilon = 0.00001;
105 
106     double const dx = GetX() - other.GetX();
107     double const dy = GetY() - other.GetY();
108     double const dz = GetZ() - other.GetZ();
109 
110     // TODO: Should we compare other data members, besides the coordinates?
111 
112     if ((dx <= epsilon && dx >= -epsilon)
113      && (dy <= epsilon && dy >= -epsilon)
114      && (dz <= epsilon && dz >= -epsilon))
115     {
116         return true;
117     }
118 
119     // If we do other members
120     // bool compare_classification(uint8_t cls, uint8_t expected)
121     // {
122     //    // 31 is max index in classification lookup table
123     //    clsidx = (cls & 31);
124     //    assert(clsidx <= 31);
125     //    return (clsidx == expected);
126     // }
127 
128     return false;
129 }
130 
Validate() const131 bool Point::Validate() const
132 {
133     unsigned int flags = 0;
134 
135     if (this->GetReturnNumber() > 0x07)
136         flags |= eReturnNumber;
137 
138     if (this->GetNumberOfReturns() > 0x07)
139         flags |= eNumberOfReturns;
140 
141     if (this->GetScanDirection() > 0x01)
142         flags |= eScanDirection;
143 
144     if (this->GetFlightLineEdge() > 0x01)
145         flags |= eFlightLineEdge;
146 
147     if (eScanAngleRankMin > this->GetScanAngleRank()
148         || this->GetScanAngleRank() > eScanAngleRankMax)
149     {
150         flags |= eScanAngleRank;
151     }
152 
153     if (flags > 0)
154     {
155         throw invalid_point_data("point data members out of range", flags);
156     }
157 
158     return true;
159 }
160 
IsValid() const161 bool Point::IsValid() const
162 {
163 
164     if (eScanAngleRankMin > this->GetScanAngleRank() || this->GetScanAngleRank() > eScanAngleRankMax)
165         return false;
166 
167     if (this->GetFlightLineEdge() > 0x01)
168         return false;
169 
170     if (this->GetScanDirection() > 0x01)
171         return false;
172 
173     if (this->GetNumberOfReturns() > 0x07)
174         return false;
175 
176     if (this->GetReturnNumber() > 0x07)
177         return false;
178 
179     return true;
180 }
181 
SetHeader(Header const * header)182 void Point::SetHeader(Header const* header)
183 {
184 
185     if (!header)
186     {
187         throw liblas_error("header reference for SetHeader is void");
188     }
189 
190     // If we don't have a header initialized, set the point's to the
191     // one we were given.
192     if (!m_header) m_header = header;
193 
194     // This is hopefully faster than copying everything if we don't have
195     // any data set and nothing to worry about.
196     const liblas::Schema* schema;
197     uint16_t wanted_length = header->GetDataRecordLength();
198     schema = &(header->GetSchema());
199     uint32_t sum = std::accumulate(m_data.begin(), m_data.end(), 0);
200 
201     if (!sum) {
202         std::vector<uint8_t> data;
203         data.resize(wanted_length);
204         data.assign(wanted_length, 0);
205         m_data = data;
206         m_header = header;
207         return;
208     }
209 
210     bool bApplyNewScaling = true;
211 
212 
213 
214     if (detail::compare_distance(header->GetScaleX(), m_header->GetScaleX()) &&
215         detail::compare_distance(header->GetScaleY(), m_header->GetScaleY()) &&
216         detail::compare_distance(header->GetScaleZ(), m_header->GetScaleZ()) &&
217         detail::compare_distance(header->GetOffsetX(), m_header->GetOffsetX()) &&
218         detail::compare_distance(header->GetOffsetY(), m_header->GetOffsetY()) &&
219         detail::compare_distance(header->GetOffsetZ(), m_header->GetOffsetZ()))
220         bApplyNewScaling = false;
221     else
222         bApplyNewScaling = true;
223 
224 
225 
226     if (wanted_length != m_data.size())
227     {
228         // Manually copy everything but the header ptr
229         // We can't just copy the raw data because its
230         // layout is likely changing as a result of the
231         // schema change.
232         Point p(*this);
233         m_header = header;
234 
235         std::vector<uint8_t> data;
236         data.resize(wanted_length);
237         data.assign(wanted_length, 0);
238         m_data = data;
239         m_header = header;
240 
241         SetX(p.GetX());
242         SetY(p.GetY());
243         SetZ(p.GetZ());
244 
245         SetIntensity(p.GetIntensity());
246         SetScanFlags(p.GetScanFlags());
247         SetClassification(p.GetClassification());
248         SetScanAngleRank(p.GetScanAngleRank());
249         SetUserData(p.GetUserData());
250         SetPointSourceID(p.GetPointSourceID());
251 
252         boost::optional< Dimension const& > t = schema->GetDimension("Time");
253         if (t)
254             SetTime(p.GetTime());
255 
256         boost::optional< Dimension const& > c = schema->GetDimension("Red");
257         if (c)
258             SetColor(p.GetColor());
259 
260         // FIXME: copy other custom dimensions here?  resetting the
261         // headerptr can be catastrophic in a lot of cases.
262     }
263 
264 
265     // The header's scale/offset can change the raw storage of xyz.
266     // SetHeader can result in a rescaling of the data.
267     double x;
268     double y;
269     double z;
270 
271     if (bApplyNewScaling)
272     {
273         x = GetX();
274         y = GetY();
275         z = GetZ();
276     }
277 
278     m_header = header;
279 
280     if (bApplyNewScaling)
281     {
282         SetX(x);
283         SetY(y);
284         SetZ(z);
285     }
286 
287 
288 }
GetHeader() const289 Header const* Point::GetHeader() const
290 {
291     if (m_header) return m_header; else return &m_default_header;
292 }
293 
GetPTree() const294 liblas::property_tree::ptree Point::GetPTree() const
295 {
296     using liblas::property_tree::ptree;
297     ptree pt;
298 
299     pt.put("x", GetX());
300     pt.put("y", GetY());
301     pt.put("z", GetZ());
302 
303     pt.put("rawx", GetRawX());
304     pt.put("rawy", GetRawY());
305     pt.put("rawz", GetRawZ());
306 
307     pt.put("time", GetTime());
308     pt.put("intensity", GetIntensity());
309     pt.put("returnnumber", GetReturnNumber());
310     pt.put("numberofreturns", GetNumberOfReturns());
311     pt.put("scandirection", GetScanDirection());
312 
313     pt.put("scanangle", GetScanAngleRank());
314     pt.put("flightlineedge", GetFlightLineEdge());
315 
316     pt.put("userdata", GetUserData());
317     pt.put("pointsourceid", GetPointSourceID());
318 
319     ptree klasses;
320 
321     liblas::Classification const& c = GetClassification();
322 
323 // I hate you windows
324 #ifdef _MSC_VER
325 #ifdef GetClassName
326 #undef GetClassName
327 #endif
328 #endif
329     std::string name = c.GetClassName();
330 
331     klasses.put("name", name);
332     klasses.put("id", c.GetClass());
333     klasses.put("withheld", c.IsWithheld());
334     klasses.put("keypoint", c.IsKeyPoint());
335     klasses.put("synthetic", c.IsSynthetic());
336 
337     pt.add_child("classification",klasses);
338 
339     ptree colors;
340     liblas::Color const& clr = GetColor();
341 
342     colors.put("red", clr.GetRed());
343     colors.put("green", clr.GetGreen());
344     colors.put("blue", clr.GetBlue());
345     pt.add_child("color", colors);
346 
347     return pt;
348 }
349 
operator <<(std::ostream & os,liblas::Point const & p)350 std::ostream& operator<<(std::ostream& os, liblas::Point const& p)
351 {
352     using liblas::property_tree::ptree;
353     ptree tree = p.GetPTree();
354 
355     os << "---------------------------------------------------------" << std::endl;
356 
357     os.setf(std::ios_base::fixed, std::ios_base::floatfield);
358     os.precision(6);
359 
360     os << "  X: \t\t\t" << tree.get<double>("x") << std::endl;
361     os << "  Y: \t\t\t" << tree.get<double>("y") << std::endl;
362     os << "  Z: \t\t\t" << tree.get<double>("z") << std::endl;
363     os << "  Time: \t\t" << tree.get<double>("time") << std::endl;
364     os.unsetf(std::ios_base::fixed);
365     os.unsetf(std::ios_base::floatfield);
366     os << "  Return Number: \t" << tree.get<uint32_t>("returnnumber") << std::endl;
367     os << "  Return Count: \t" << tree.get<uint32_t>("numberofreturns") << std::endl;
368     os << "  Flightline Edge: \t" << tree.get<uint32_t>("flightlineedge") << std::endl;
369     os << "  Intensity: \t\t" << tree.get<uint32_t>("intensity") << std::endl;
370     os << "  Scan Direction: \t" << tree.get<uint32_t>("scandirection") << std::endl;
371     os << "  Scan Angle Rank: \t" << tree.get<int32_t>("scanangle") << std::endl;
372     os << "  Classification: \t" << tree.get<std::string>("classification.name") << std::endl;
373     os << "         witheld: \t" << tree.get<std::string>("classification.withheld") << std::endl;
374     os << "        keypoint: \t" << tree.get<std::string>("classification.keypoint") << std::endl;
375     os << "       synthetic: \t" << tree.get<std::string>("classification.synthetic") << std::endl;
376     os << "  RGB Color: \t\t" << tree.get<uint32_t>("color.red") << " "
377                               << tree.get<uint32_t>("color.green") << " "
378                               << tree.get<uint32_t>("color.blue") << std::endl;
379     os << "---------------------------------------------------------" << std::endl;
380 
381     return os;
382 }
383 
384 
GetX() const385 double Point::GetX() const
386 {
387     int32_t v = GetRawX();
388 
389     double output  = (v * GetHeader()->GetScaleX()) + GetHeader()->GetOffsetX();
390 
391     return output;
392 }
393 
GetY() const394 double Point::GetY() const
395 {
396     int32_t v = GetRawY();
397 
398     double output = (v * GetHeader()->GetScaleY()) + GetHeader()->GetOffsetY();
399     return output;
400 }
401 
GetZ() const402 double Point::GetZ() const
403 {
404     int32_t v = GetRawZ();
405 
406     double output = 0;
407 
408     output  = (v * GetHeader()->GetScaleZ()) + GetHeader()->GetOffsetZ();
409 
410     return output;
411 }
412 
SetX(double const & value)413 void Point::SetX( double const& value )
414 {
415     int32_t v;
416     double scale;
417     double offset;
418 
419     scale = GetHeader()->GetScaleX();
420     offset = GetHeader()->GetOffsetX();
421 
422     // descale the value given our scale/offset
423     v = static_cast<int32_t>(
424                          detail::sround((value - offset) / scale));
425     SetRawX(v);
426 }
427 
SetY(double const & value)428 void Point::SetY( double const& value )
429 {
430     int32_t v;
431     double scale;
432     double offset;
433 
434     scale = GetHeader()->GetScaleY();
435     offset = GetHeader()->GetOffsetY();
436 
437 
438     // descale the value given our scale/offset
439     v = static_cast<int32_t>(
440                          detail::sround((value - offset) / scale));
441     SetRawY(v);
442 }
443 
SetZ(double const & value)444 void Point::SetZ( double const& value )
445 {
446     int32_t v;
447     double scale;
448     double offset;
449 
450     scale = GetHeader()->GetScaleZ();
451     offset = GetHeader()->GetOffsetZ();
452 
453     // descale the value given our scale/offset
454     v = static_cast<int32_t>(
455                          detail::sround((value - offset) / scale));
456     SetRawZ(v);
457 }
458 
GetRawX() const459 int32_t Point::GetRawX() const
460 {
461     // std::vector<uint8_t>::size_type pos = GetDimensionPosition("X");
462     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(0);
463     std::vector<uint8_t>::size_type pos = 0;
464 
465 #ifdef LIBLAS_ENDIAN_AWARE
466     int32_t output = liblas::detail::bitsToInt<int32_t>(output, m_data, pos);
467     return output;
468 #else
469     uint8_t* data = const_cast<uint8_t*>(&m_data[0] + pos);
470     int32_t* output = reinterpret_cast<int32_t*>(data);
471     return *output;
472 #endif
473 }
474 
GetRawY() const475 int32_t Point::GetRawY() const
476 {
477     // std::vector<uint8_t>::size_type pos = GetDimensionPosition("Y");
478     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(1);
479     std::vector<uint8_t>::size_type pos = 4;
480 
481 #ifdef LIBLAS_ENDIAN_AWARE
482     int32_t output = liblas::detail::bitsToInt<int32_t>(output, m_data, pos);
483     return output;
484 #else
485     uint8_t* data = const_cast<uint8_t*>(&m_data[0] + pos);
486     int32_t* output = reinterpret_cast<int32_t*>(data);
487     return *output;
488 #endif
489 }
490 
GetRawZ() const491 int32_t Point::GetRawZ() const
492 {
493     // std::vector<uint8_t>::size_type pos = GetDimensionPosition("Z");
494     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(2);
495     std::vector<uint8_t>::size_type pos = 8;
496 
497 #ifdef LIBLAS_ENDIAN_AWARE
498     int32_t output = liblas::detail::bitsToInt<int32_t>(output, m_data, pos);
499     return output;
500 #else
501     uint8_t* data = const_cast<uint8_t*>(&m_data[0] + pos);
502     int32_t* output = reinterpret_cast<int32_t*>(data);
503     return *output;
504 #endif
505 }
506 
SetRawX(int32_t const & value)507 void Point::SetRawX( int32_t const& value)
508 {
509     // std::vector<uint8_t>::size_type pos = GetDimensionPosition("X");
510     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(0);
511     std::vector<uint8_t>::size_type pos = 0;
512     liblas::detail::intToBits<int32_t>(value, m_data, pos);
513 }
514 
SetRawY(int32_t const & value)515 void Point::SetRawY( int32_t const& value)
516 {
517     // std::vector<uint8_t>::size_type pos = GetDimensionPosition("Y");
518     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(1);
519     std::vector<uint8_t>::size_type pos = 4;
520     liblas::detail::intToBits<int32_t>(value, m_data, pos);
521 }
522 
SetRawZ(int32_t const & value)523 void Point::SetRawZ( int32_t const& value)
524 {
525     // std::vector<uint8_t>::size_type pos = GetDimensionPosition("Z");
526     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(2);
527     std::vector<uint8_t>::size_type pos = 8;
528     liblas::detail::intToBits<int32_t>(value, m_data, pos);
529 }
530 
GetIntensity() const531 uint16_t Point::GetIntensity() const
532 {
533     // Intensity's position is always the 4th dimension
534     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(3);
535     std::vector<uint8_t>::size_type pos = 12;
536 
537 #ifdef LIBLAS_ENDIAN_AWARE
538     uint16_t output = liblas::detail::bitsToInt<uint16_t>(output, m_data, pos);
539     return output;
540 #else
541     uint8_t* data = const_cast<uint8_t*>(&m_data[0] + pos);
542     uint16_t* output = reinterpret_cast<uint16_t*>(data);
543     return *output;
544 #endif
545 
546 }
547 
548 
SetIntensity(uint16_t const & intensity)549 void Point::SetIntensity(uint16_t const& intensity)
550 {
551     // Intensity's position is always the 4th dimension
552     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(3);
553     std::vector<uint8_t>::size_type pos = 12;
554     liblas::detail::intToBits<uint16_t>(intensity,
555                                                m_data,
556                                                pos);
557 }
558 
GetScanFlags() const559 uint8_t Point::GetScanFlags() const
560 {
561     // Scan Flag's position is always the 5th dimension
562     // (the entire byte composed of "Return Number", "Number of Returns",
563     // "Scan Direction", and "Flightline Edge")
564     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(4);
565     std::vector<uint8_t>::size_type pos = 14;
566     return m_data[pos];
567 }
568 
SetScanFlags(uint8_t const & flags)569 void Point::SetScanFlags(uint8_t const& flags)
570 {
571     // Scan Flag's position is always the 5th dimension
572     // (the entire byte composed of "Return Number", "Number of Returns",
573     // "Scan Direction", and "Flightline Edge")
574     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(4);
575     std::vector<uint8_t>::size_type pos = 14;
576     m_data[pos] = flags;
577 }
578 
GetReturnNumber() const579 uint16_t Point::GetReturnNumber() const
580 {
581     // "Return Number" is always the 5th dimension
582     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(4);
583     std::vector<uint8_t>::size_type pos = 14;
584 
585     uint8_t flags = m_data[pos];
586 
587     // Read bits 1,2,3 (first 3 bits)
588     return (flags & 0x07);
589 }
590 
SetReturnNumber(uint16_t const & num)591 void Point::SetReturnNumber(uint16_t const& num)
592 {
593     // "Return Number" is always the 5th dimension
594     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(4);
595     std::vector<uint8_t>::size_type pos = 14;
596     uint8_t flags = m_data[pos];
597 
598     // Store value in bits 0,1,2
599     uint8_t mask = 0x7 << 0; // 0b00000111
600     flags &= ~mask;
601     flags |= mask & (static_cast<uint8_t>(num) << 0);
602     m_data[pos] = flags;
603 }
604 
GetNumberOfReturns() const605 uint16_t Point::GetNumberOfReturns() const
606 {
607     uint8_t flags;
608 
609     // "Number of Returns" is always the 6th dimension
610     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(5);
611     std::vector<uint8_t>::size_type pos = 14;
612 
613     flags = m_data[pos];
614 
615     // Read bits 4,5,6
616     return ((flags >> 3) & 0x07);
617 }
618 
SetNumberOfReturns(uint16_t const & num)619 void Point::SetNumberOfReturns(uint16_t const& num)
620 {
621     // "Number of Returns" is always the 6th dimension
622     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(5);
623     std::vector<uint8_t>::size_type pos = 14;
624 
625     uint8_t flags = m_data[pos];
626 
627     // Store value in bits 3,4,5
628     uint8_t mask = 0x7 << 3; // 0b00111000
629     flags &= ~mask;
630     flags |= mask & (static_cast<uint8_t>(num) << 3);
631     m_data[pos] = flags;
632 
633 }
634 
SetScanDirection(uint16_t const & dir)635 void Point::SetScanDirection(uint16_t const& dir)
636 {
637     // "Scan Direction" is always the 7th dimension
638     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(6);
639     std::vector<uint8_t>::size_type pos = 14;
640 
641     uint8_t flags = m_data[pos];
642 
643     // Store value in bits 6
644     uint8_t mask = 0x1 << 6; // 0b01000000
645     flags &= ~mask;
646     flags |= mask & (static_cast<uint8_t>(dir) << 6);
647     m_data[pos] = flags;
648 }
649 
GetScanDirection() const650 uint16_t Point::GetScanDirection() const
651 {
652     // "Scan Direction" is always the 7th dimension
653     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(6);
654     std::vector<uint8_t>::size_type pos = 14;
655     uint8_t flags = m_data[pos];
656 
657     // Read 6th bit
658     return ((flags >> 6) & 0x01);
659 }
660 
SetFlightLineEdge(uint16_t const & edge)661 void Point::SetFlightLineEdge(uint16_t const& edge)
662 {
663     // "Flightline Edge" is always the 8th dimension
664     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(7);
665     std::vector<uint8_t>::size_type pos = 14;
666 
667     uint8_t flags = m_data[pos];
668 
669     // Store value in bits 7
670     uint8_t mask = 0x1 << 7; // 0b10000000
671     flags &= ~mask;
672     flags |= mask & (static_cast<uint8_t>(edge) << 7);
673     m_data[pos] = flags;
674 
675 }
676 
GetFlightLineEdge() const677 uint16_t Point::GetFlightLineEdge() const
678 {
679     // "Flightline Edge" is always the 8th dimension
680     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(7);
681     std::vector<uint8_t>::size_type pos = 14;
682 
683     uint8_t flags = m_data[pos];
684 
685     // Read 8th bit
686     return ((flags >> 7) & 0x01);
687 }
688 
689 
GetClassification() const690 Classification Point::GetClassification() const
691 {
692     // "Classification" is always the 9th dimension
693     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(8);
694     std::vector<uint8_t>::size_type pos = 15;
695 
696     uint8_t kls = m_data[pos];
697     return Classification(kls);
698 }
699 
SetClassification(Classification const & cls)700 void Point::SetClassification(Classification const& cls)
701 {
702     // "Classification" is always the 9th dimension
703     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(8);
704     std::vector<uint8_t>::size_type const pos = 15;
705     m_data[pos] = static_cast<uint8_t>(cls.GetFlags().to_ulong());
706 }
707 
SetClassification(Classification::bitset_type const & flags)708 void Point::SetClassification(Classification::bitset_type const& flags)
709 {
710     // "Classification" is always the 9th dimension
711     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(8);
712     std::vector<uint8_t>::size_type const pos = 15;
713     m_data[pos] = static_cast<uint8_t>(flags.to_ulong());
714 }
715 
SetClassification(uint8_t const & flags)716 void Point::SetClassification(uint8_t const& flags)
717 {
718     // "Classification" is always the 9th dimension
719     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(8);
720     std::vector<uint8_t>::size_type const  pos = 15;
721     m_data[pos] = flags;
722 }
723 
SetScanAngleRank(int8_t const & rank)724 void Point::SetScanAngleRank(int8_t const& rank)
725 {
726     // "Scan Angle Rank" is always the 10th dimension
727     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(9);
728     std::vector<uint8_t>::size_type pos = 16;
729 
730     m_data[pos] = rank;
731 
732 }
GetScanAngleRank() const733 int8_t Point::GetScanAngleRank() const
734 {
735     // "Scan Angle Rank" is always the 10th dimension
736     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(9);
737     std::vector<uint8_t>::size_type pos = 16;
738 
739     return m_data[pos];
740 }
741 
GetUserData() const742 uint8_t Point::GetUserData() const
743 {
744     // "User Data" is always the 11th dimension
745     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(10);
746     std::vector<uint8_t>::size_type pos = 17;
747     return m_data[pos];
748 }
749 
SetUserData(uint8_t const & flags)750 void Point::SetUserData(uint8_t const& flags)
751 {
752     // "User Data" is always the 11th dimension
753     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(10);
754     std::vector<uint8_t>::size_type pos = 17;
755     m_data[pos] = flags;
756 }
757 
GetPointSourceID() const758 uint16_t Point::GetPointSourceID() const
759 {
760 
761     // "Point Source ID" is always the 12th dimension
762     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(11);
763     std::vector<uint8_t>::size_type pos = 18;
764 
765 #ifdef LIBLAS_ENDIAN_AWARE
766     uint16_t output = liblas::detail::bitsToInt<uint16_t>(output, m_data, pos);
767     return output;
768 #else
769     uint8_t* data = const_cast<uint8_t*>(&m_data[0] + pos);
770     uint16_t* output = reinterpret_cast<uint16_t*>(data);
771     return *output;
772 #endif
773 
774 }
775 
SetPointSourceID(uint16_t const & id)776 void Point::SetPointSourceID(uint16_t const& id)
777 {
778     // "Point Source ID" is always the 12th dimension
779     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(11);
780     std::vector<uint8_t>::size_type pos = 18;
781     liblas::detail::intToBits<uint16_t>(id, m_data, pos);
782 }
783 
SetTime(double const & t)784 void Point::SetTime(double const& t)
785 {
786 
787     // "Time" is the 13th dimension if it exists
788     // std::size_t index_pos = 12;
789 
790     PointFormatName f = GetHeader()->GetDataFormatId();
791 
792     if ( f == ePointFormat0 || f == ePointFormat2 ) {
793         std::ostringstream msg;
794         msg << "Point::SetTime - Unable to set time for ePointFormat0 or ePointFormat2, "
795             << "no Time dimension exists on this format";
796         throw liblas::invalid_format(msg.str());
797     }
798 
799     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(index_pos);
800     std::vector<uint8_t>::size_type pos = 20;
801     detail::binary::endian_value<double> value(t);
802     value.store<detail::binary::little_endian_tag>(&m_data[0] + pos);
803 }
804 
GetTime() const805 double Point::GetTime() const
806 {
807     PointFormatName f = GetHeader()->GetDataFormatId();
808 
809     if (f == ePointFormat0 || f == ePointFormat2)
810     {
811         // std::ostringstream msg;
812         // msg << "Point::GetTime - Unable to get time for ePointFormat0 or ePointFormat2, "
813         //     << "no Time dimension exists on this format";
814         // throw std::runtime_error(msg.str());
815         return 0.0;
816     }
817 
818     // "Time" is the 13th dimension if it exists
819     // std::size_t const index_pos = 12;
820     // std::vector<uint8_t>::size_type pos = GetDimensionBytePosition(index_pos);
821     std::vector<uint8_t>::size_type pos = 20;
822 
823 #ifdef LIBLAS_ENDIAN_AWARE
824     detail::binary::endian_value<double> output;
825     output.load<detail::binary::little_endian_tag>(&m_data[0] + pos);
826     return output;
827 #else
828     uint8_t* data = const_cast<uint8_t*>(&m_data[0] + pos);
829     double* output = reinterpret_cast<double*>(data);
830     return *output;
831 #endif
832 
833 }
834 
GetColor() const835 Color Point::GetColor() const
836 {
837 
838 
839     // "Color" starts at the 14th dimension if it exists
840     // std::size_t index_pos = 13;
841 
842     Color color;
843     PointFormatName f = GetHeader()->GetDataFormatId();
844 
845     if ( f == ePointFormat0 || f == ePointFormat1 ) {
846         return color;
847     }
848 
849     assert(!(f == ePointFormat0 || f == ePointFormat1));
850 
851 
852     std::size_t index_pos = 20;
853 
854     if (f == ePointFormat3)
855         index_pos = index_pos + 8; // increment to include position of Time.
856 
857 
858 
859     std::vector<uint8_t>::size_type red_pos = index_pos;
860     std::vector<uint8_t>::size_type green_pos = index_pos + 2;
861     std::vector<uint8_t>::size_type blue_pos = index_pos + 4;
862 
863     assert(red_pos <= m_data.size());
864     assert(blue_pos <= m_data.size());
865     assert(green_pos <= m_data.size());
866 
867 #ifdef LIBLAS_ENDIAN_AWARE
868     using liblas::detail::bitsToInt;
869     uint16_t red(0);
870     uint16_t green(0);
871     uint16_t blue(0);
872     red = bitsToInt<uint16_t>(red, m_data, red_pos);
873     green = bitsToInt<uint16_t>(green, m_data, green_pos);
874     blue = bitsToInt<uint16_t>(blue, m_data, blue_pos);
875     color[0] = red;
876     color[1] = green;
877     color[2] = blue;
878 #else
879     uint8_t* red_data = const_cast<uint8_t*>(&m_data[0] + red_pos);
880     uint16_t* p_red = reinterpret_cast<uint16_t*>(red_data);
881 
882     uint8_t* green_data = const_cast<uint8_t*>(&m_data[0] + green_pos);
883     uint16_t* p_green = reinterpret_cast<uint16_t*>(green_data);
884 
885     uint8_t* blue_data = const_cast<uint8_t*>(&m_data[0] + blue_pos);
886     uint16_t* p_blue = reinterpret_cast<uint16_t*>(blue_data);
887 
888     color[0] = *p_red;
889     color[1] = *p_green;
890     color[2] = *p_blue;
891 #endif
892 
893     return color;
894 
895 
896 }
897 
SetColor(Color const & value)898 void Point::SetColor(Color const& value)
899 {
900 
901     // "Color" starts at the 14th dimension if it exists
902     // std::size_t index_pos = 13;
903 
904     PointFormatName f = GetHeader()->GetDataFormatId();
905 
906     if ( f == ePointFormat0 || f == ePointFormat1 ) {
907         std::ostringstream msg;
908         msg << "Point::SetColor - Unable to set color for ePointFormat0 or ePointFormat1, "
909             << "no Color dimension exists on this format";
910         throw liblas::invalid_format(msg.str());
911     }
912 
913     if ( m_data.size() == ePointFormat0 || f == ePointFormat1 ) {
914         std::ostringstream msg;
915         msg << "Point::SetColor - Unable to set color for ePointFormat0 or ePointFormat1, "
916             << "no Color dimension exists on this format";
917         throw liblas::invalid_format(msg.str());
918     }
919 
920     using liblas::detail::intToBits;
921 
922     std::size_t index_pos = 20;
923 
924     if (f == ePointFormat3)
925         index_pos = 28; // increment to include position of Time.
926     if (f == ePointFormat2)
927         index_pos = 20; // increment to include position of Time.
928 
929 
930     std::vector<uint8_t>::size_type red_pos = index_pos;
931     std::vector<uint8_t>::size_type green_pos = index_pos + 2;
932     std::vector<uint8_t>::size_type blue_pos = index_pos + 4;
933 
934     assert(red_pos <= m_data.size());
935     assert(blue_pos <= m_data.size());
936     assert(green_pos <= m_data.size());
937 
938     intToBits<uint16_t>(value.GetRed(), m_data, red_pos);
939     intToBits<uint16_t>(value.GetGreen(), m_data, green_pos);
940     intToBits<uint16_t>(value.GetBlue(), m_data, blue_pos);
941 }
942 
GetDimensionBytePosition(std::size_t dim_pos) const943 std::vector<uint8_t>::size_type Point::GetDimensionBytePosition(std::size_t dim_pos) const
944 {
945     optional<Dimension const&> d;
946     d = m_header->GetSchema().GetDimension(dim_pos);
947 
948     if (!d)
949     {
950         std::ostringstream oss;
951         oss <<"Dimension at position " << dim_pos << " not found";
952         throw liblas_error(oss.str());
953     }
954     return d->GetByteOffset();
955 }
956 
GetValue(Dimension const & d) const957 boost::any Point::GetValue(Dimension const& d) const
958 {
959     boost::any output;
960     boost::ignore_unused_variable_warning(d);
961 
962     return output;
963 }
964 
965 } // namespace liblas
966