1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  libLAS - http://liblas.org - A BSD library for LAS format data.
5  * Purpose:  LAS 1.2 writer implementation for C++ libLAS
6  * Author:   Howard Butler, hobu.inc@gmail.com
7  *
8  ******************************************************************************
9  * Copyright (c) 2008, Howard Butler
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 <liblas12/detail/writer12.hpp>
43 #include <liblas12/detail/utility.hpp>
44 #include <liblas12/lasheader.hpp>
45 #include <liblas12/laspoint.hpp>
46 #include <liblas12/liblas.hpp>
47 // std
48 #include <vector>
49 #include <fstream>
50 #include <stdexcept>
51 #include <cstdlib> // std::size_t
52 #include <cassert>
53 
54 namespace liblas { namespace detail { namespace v12 {
55 
WriterImpl(std::ostream & ofs)56 WriterImpl::WriterImpl(std::ostream& ofs) :
57     Base(ofs), m_pointCount(0)
58 {
59 }
60 
GetVersion() const61 std::size_t WriterImpl::GetVersion() const
62 {
63     return eLASVersion12;
64 }
65 
WriteHeader(LASHeader & header)66 void WriterImpl::WriteHeader(LASHeader& header)
67 {
68     uint8_t n1 = 0;
69     uint16_t n2 = 0;
70     uint32_t n4 = 0;
71 
72     // Rewrite the georeference VLR entries if they exist
73     header.SetGeoreference();
74 
75     // Seek to the beginning
76     m_ofs.seekp(0, std::ios::beg);
77     std::ios::pos_type beginning = m_ofs.tellp();
78 
79     // Seek to the end
80     m_ofs.seekp(0, std::ios::end);
81     std::ios::pos_type end = m_ofs.tellp();
82 
83     // Figure out how many points we already have.  Each point record
84     // should be 20 bytes long, and header.GetDataOffset tells
85     // us the location to start counting points from.
86 
87     // This test should only be true if we were opened in both
88     // std::ios::in *and* std::ios::out, otherwise it should return false
89     // and we won't adjust the point count.
90 
91     if (beginning != end)
92     {
93         m_pointCount = static_cast<uint32_t>(end) - header.GetDataOffset();
94         m_pointCount /= header.GetDataRecordLength();
95 
96         // Position to the beginning of the file to start writing the header
97         m_ofs.seekp(0, std::ios::beg);
98     }
99 
100     // 1. File Signature
101     std::string const filesig(header.GetFileSignature());
102     assert(filesig.size() == 4);
103     detail::write_n(m_ofs, filesig, 4);
104 
105     // 2. File Source ID
106     n2 = header.GetFileSourceId();
107     detail::write_n(m_ofs, n2, sizeof(n2));
108 
109     // 3. Reserved
110     n2 = header.GetReserved();
111     detail::write_n(m_ofs, n2, sizeof(n2));
112 
113     // 4-7. GUID data
114     uint32_t d1 = 0;
115     uint16_t d2 = 0;
116     uint16_t d3 = 0;
117     uint8_t d4[8] = { 0 };
118     liblas::guid g = header.GetProjectId();
119     g.output_data(d1, d2, d3, d4);
120     detail::write_n(m_ofs, d1, sizeof(d1));
121     detail::write_n(m_ofs, d2, sizeof(d2));
122     detail::write_n(m_ofs, d3, sizeof(d3));
123     detail::write_n(m_ofs, d4, sizeof(d4));
124 
125     // 8. Version major
126     n1 = header.GetVersionMajor();
127     assert(1 == n1);
128     detail::write_n(m_ofs, n1, sizeof(n1));
129 
130     // 9. Version minor
131     n1 = header.GetVersionMinor();
132     assert(2 == n1);
133     detail::write_n(m_ofs, n1, sizeof(n1));
134 
135     // 10. System ID
136     std::string const sysid(header.GetSystemId(true));
137     assert(sysid.size() == 32);
138     detail::write_n(m_ofs, sysid, 32);
139 
140     // 11. Generating Software ID
141     std::string const softid(header.GetSoftwareId(true));
142     assert(softid.size() == 32);
143     detail::write_n(m_ofs, softid, 32);
144 
145     // 12. File Creation Day of Year
146     n2 = header.GetCreationDOY();
147     detail::write_n(m_ofs, n2, sizeof(n2));
148 
149     // 13. File Creation Year
150     n2 = header.GetCreationYear();
151     detail::write_n(m_ofs, n2, sizeof(n2));
152 
153     // 14. Header Size
154     n2 = header.GetHeaderSize();
155     assert(227 <= n2);
156     detail::write_n(m_ofs, n2, sizeof(n2));
157 
158     // 15. Offset to data
159     // At this point, no variable length records are written,
160     // so  data offset is equal to header size (227)
161     // TODO: This value must be updated after new variable length record is added.
162     n4 = header.GetDataOffset();
163     detail::write_n(m_ofs, n4, sizeof(n4));
164 
165     // 16. Number of variable length records
166     // TODO: This value must be updated after new variable length record is added.
167     n4 = header.GetRecordsCount();
168     detail::write_n(m_ofs, n4, sizeof(n4));
169 
170     // 17. Point Data Format ID
171     n1 = static_cast<uint8_t>(header.GetDataFormatId());
172     detail::write_n(m_ofs, n1, sizeof(n1));
173 
174     // 18. Point Data Record Length
175     n2 = header.GetDataRecordLength();
176     detail::write_n(m_ofs, n2, sizeof(n2));
177 
178     // 19. Number of point records
179     // This value is updated if necessary, see UpdateHeader function.
180     n4 = header.GetPointRecordsCount();
181     detail::write_n(m_ofs, n4, sizeof(n4));
182 
183     // 20. Number of points by return
184     std::vector<uint32_t>::size_type const srbyr = 5;
185     std::vector<uint32_t> const& vpbr = header.GetPointRecordsByReturnCount();
186     assert(vpbr.size() <= srbyr);
187     uint32_t pbr[srbyr] = { 0 };
188     std::copy(vpbr.begin(), vpbr.end(), pbr);
189     detail::write_n(m_ofs, pbr, sizeof(pbr));
190 
191     // 21-23. Scale factors
192     detail::write_n(m_ofs, header.GetScaleX(), sizeof(double));
193     detail::write_n(m_ofs, header.GetScaleY(), sizeof(double));
194     detail::write_n(m_ofs, header.GetScaleZ(), sizeof(double));
195 
196     // 24-26. Offsets
197     detail::write_n(m_ofs, header.GetOffsetX(), sizeof(double));
198     detail::write_n(m_ofs, header.GetOffsetY(), sizeof(double));
199     detail::write_n(m_ofs, header.GetOffsetZ(), sizeof(double));
200 
201     // 27-28. Max/Min X
202     detail::write_n(m_ofs, header.GetMaxX(), sizeof(double));
203     detail::write_n(m_ofs, header.GetMinX(), sizeof(double));
204 
205     // 29-30. Max/Min Y
206     detail::write_n(m_ofs, header.GetMaxY(), sizeof(double));
207     detail::write_n(m_ofs, header.GetMinY(), sizeof(double));
208 
209     // 31-32. Max/Min Z
210     detail::write_n(m_ofs, header.GetMaxZ(), sizeof(double));
211     detail::write_n(m_ofs, header.GetMinZ(), sizeof(double));
212 
213     // If WriteVLR returns a value, it is because the header's
214     // offset is not large enough to contain the VLRs.  The value
215     // it returns is the number of bytes we must increase the header
216     // by in order for it to contain the VLRs.
217     int32_t difference = WriteVLR(header);
218     if (difference < 0) {
219         header.SetDataOffset(header.GetDataOffset() + abs(difference) );
220         WriteVLR(header);
221 
222         // Make sure to rewrite the dataoffset in the header portion now that
223         // we've changed it.
224         std::streamsize const current_pos = m_ofs.tellp();
225         std::streamsize const offset_pos = 96;
226         m_ofs.seekp(offset_pos, std::ios::beg);
227         detail::write_n(m_ofs, header.GetDataOffset() , sizeof(header.GetDataOffset()));
228         m_ofs.seekp(current_pos, std::ios::beg);
229     }
230 
231     // If we already have points, we're going to put it at the end of the file.
232     // If we don't have any points,  we're going to leave it where it is.
233     if (m_pointCount != 0)
234     {
235         m_ofs.seekp(0, std::ios::end);
236     }
237 }
238 
UpdateHeader(LASHeader const & header)239 void WriterImpl::UpdateHeader(LASHeader const& header)
240 {
241     if (m_pointCount != header.GetPointRecordsCount())
242     {
243         // Skip to first byte of number of point records data member
244         std::streamsize const dataPos = 107;
245         m_ofs.seekp(dataPos, std::ios::beg);
246 
247         detail::write_n(m_ofs, m_pointCount , sizeof(m_pointCount));
248     }
249 }
250 
WritePointRecord(LASPoint const & point,LASHeader const & header)251 void WriterImpl::WritePointRecord(LASPoint const& point, LASHeader const& header)
252 {
253     // TODO: Static assert would be better
254 
255     double t = 0;
256     uint16_t red = 0;
257     uint16_t blue = 0;
258     uint16_t green = 0;
259     LASColor color;
260 
261     assert(LASHeader::ePointSize0 == sizeof(m_record));
262     Writer::FillPointRecord(m_record, point, header);
263     detail::write_n(m_ofs, m_record, sizeof(m_record));
264 
265     if (header.GetDataFormatId() == LASHeader::ePointFormat1)
266     {
267         t = point.GetTime();
268         detail::write_n(m_ofs, t, sizeof(double));
269     }
270     else if (header.GetDataFormatId() == LASHeader::ePointFormat2)
271     {
272         color = point.GetColor();
273         red = color.GetRed();
274         green = color.GetGreen();
275         blue = color.GetBlue();
276         detail::write_n(m_ofs, red, sizeof(uint16_t));
277         detail::write_n(m_ofs, green, sizeof(uint16_t));
278         detail::write_n(m_ofs, blue, sizeof(uint16_t));
279     }
280     else if (header.GetDataFormatId() == LASHeader::ePointFormat3)
281     {
282         t = point.GetTime();
283         detail::write_n(m_ofs, t, sizeof(double));
284         color = point.GetColor();
285         red = color.GetRed();
286         green = color.GetGreen();
287         blue = color.GetBlue();
288         detail::write_n(m_ofs, red, sizeof(uint16_t));
289         detail::write_n(m_ofs, green, sizeof(uint16_t));
290         detail::write_n(m_ofs, blue, sizeof(uint16_t));
291     }
292 
293 
294     ++m_pointCount;
295 }
296 
297 }}} // namespace liblas::detail::v12
298 
299