1 /******************************************************************************
2 * $Id$
3 *
4 * Project: libLAS - http://liblas.org - A BSD library for LAS format data.
5 * Purpose: LAS 1.1 writer implementation 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 #include <liblas12/detail/writer11.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 v11 {
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 eLASVersion11;
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 m_pointCount = ((uint32_t) end - header.GetDataOffset())/header.GetDataRecordLength();
93
94 // Position to the beginning of the file to start writing the header
95 m_ofs.seekp(0, std::ios::beg);
96 }
97
98 // 1. File Signature
99 std::string const filesig(header.GetFileSignature());
100 assert(filesig.size() == 4);
101 detail::write_n(m_ofs, filesig, 4);
102
103 // 2. File Source ID
104 n2 = header.GetFileSourceId();
105 detail::write_n(m_ofs, n2, sizeof(n2));
106
107 // 3. Reserved
108 n2 = header.GetReserved();
109 detail::write_n(m_ofs, n2, sizeof(n2));
110
111 // 4-7. GUID data
112 uint32_t d1 = 0;
113 uint16_t d2 = 0;
114 uint16_t d3 = 0;
115 uint8_t d4[8] = { 0 };
116 liblas::guid g = header.GetProjectId();
117 g.output_data(d1, d2, d3, d4);
118 detail::write_n(m_ofs, d1, sizeof(d1));
119 detail::write_n(m_ofs, d2, sizeof(d2));
120 detail::write_n(m_ofs, d3, sizeof(d3));
121 detail::write_n(m_ofs, d4, sizeof(d4));
122
123 // 8. Version major
124 n1 = header.GetVersionMajor();
125 assert(1 == n1);
126 detail::write_n(m_ofs, n1, sizeof(n1));
127
128 // 9. Version minor
129 n1 = header.GetVersionMinor();
130 assert(1 == n1);
131 detail::write_n(m_ofs, n1, sizeof(n1));
132
133 // 10. System ID
134 std::string const sysid(header.GetSystemId(true));
135 assert(sysid.size() == 32);
136 detail::write_n(m_ofs, sysid, 32);
137
138 // 11. Generating Software ID
139 std::string const softid(header.GetSoftwareId(true));
140 assert(softid.size() == 32);
141 detail::write_n(m_ofs, softid, 32);
142
143 // 12. File Creation Day of Year
144 n2 = header.GetCreationDOY();
145 detail::write_n(m_ofs, n2, sizeof(n2));
146
147 // 13. File Creation Year
148 n2 = header.GetCreationYear();
149 detail::write_n(m_ofs, n2, sizeof(n2));
150
151 // 14. Header Size
152 n2 = header.GetHeaderSize();
153 assert(227 <= n2);
154 detail::write_n(m_ofs, n2, sizeof(n2));
155
156 // 15. Offset to data
157 // At this point, no variable length records are written,
158 // so data offset is equal to header size (227)
159 // TODO: This value must be updated after new variable length record is added.
160 n4 = header.GetDataOffset();
161 detail::write_n(m_ofs, n4, sizeof(n4));
162
163 // 16. Number of variable length records
164 // TODO: This value must be updated after new variable length record is added.
165 n4 = header.GetRecordsCount();
166 detail::write_n(m_ofs, n4, sizeof(n4));
167
168 // 17. Point Data Format ID
169 n1 = static_cast<uint8_t>(header.GetDataFormatId());
170 detail::write_n(m_ofs, n1, sizeof(n1));
171
172 // 18. Point Data Record Length
173 n2 = header.GetDataRecordLength();
174 detail::write_n(m_ofs, n2, sizeof(n2));
175
176 // 19. Number of point records
177 // This value is updated if necessary, see UpdateHeader function.
178 n4 = header.GetPointRecordsCount();
179 detail::write_n(m_ofs, n4, sizeof(n4));
180
181 // 20. Number of points by return
182 std::vector<uint32_t>::size_type const srbyr = 5;
183 std::vector<uint32_t> const& vpbr = header.GetPointRecordsByReturnCount();
184 assert(vpbr.size() <= srbyr);
185 uint32_t pbr[srbyr] = { 0 };
186 std::copy(vpbr.begin(), vpbr.end(), pbr);
187 detail::write_n(m_ofs, pbr, sizeof(pbr));
188
189 // 21-23. Scale factors
190 detail::write_n(m_ofs, header.GetScaleX(), sizeof(double));
191 detail::write_n(m_ofs, header.GetScaleY(), sizeof(double));
192 detail::write_n(m_ofs, header.GetScaleZ(), sizeof(double));
193
194 // 24-26. Offsets
195 detail::write_n(m_ofs, header.GetOffsetX(), sizeof(double));
196 detail::write_n(m_ofs, header.GetOffsetY(), sizeof(double));
197 detail::write_n(m_ofs, header.GetOffsetZ(), sizeof(double));
198
199 // 27-28. Max/Min X
200 detail::write_n(m_ofs, header.GetMaxX(), sizeof(double));
201 detail::write_n(m_ofs, header.GetMinX(), sizeof(double));
202
203 // 29-30. Max/Min Y
204 detail::write_n(m_ofs, header.GetMaxY(), sizeof(double));
205 detail::write_n(m_ofs, header.GetMinY(), sizeof(double));
206
207 // 31-32. Max/Min Z
208 detail::write_n(m_ofs, header.GetMaxZ(), sizeof(double));
209 detail::write_n(m_ofs, header.GetMinZ(), sizeof(double));
210
211 // If WriteVLR returns a value, it is because the header's
212 // offset is not large enough to contain the VLRs. The value
213 // it returns is the number of bytes we must increase the header
214 // by in order for it to contain the VLRs.
215 int32_t difference = WriteVLR(header);
216 if (difference < 0) {
217 header.SetDataOffset(header.GetDataOffset() + abs(difference) );
218 WriteVLR(header);
219
220 // Make sure to rewrite the dataoffset in the header portion now that
221 // we've changed it.
222 std::streamsize const current_pos = m_ofs.tellp();
223 std::streamsize const offset_pos = 96;
224 m_ofs.seekp(offset_pos, std::ios::beg);
225 detail::write_n(m_ofs, header.GetDataOffset() , sizeof(header.GetDataOffset()));
226 m_ofs.seekp(current_pos, std::ios::beg);
227 }
228
229 // If we already have points, we're going to put it at the end of the file.
230 // If we don't have any points, we're going to leave it where it is.
231 if (m_pointCount != 0)
232 m_ofs.seekp(0, std::ios::end);
233 }
234
UpdateHeader(LASHeader const & header)235 void WriterImpl::UpdateHeader(LASHeader const& header)
236 {
237 if (m_pointCount != header.GetPointRecordsCount())
238 {
239 // Skip to first byte of number of point records data member
240 std::streamsize const dataPos = 107;
241 m_ofs.seekp(dataPos, std::ios::beg);
242
243 detail::write_n(m_ofs, m_pointCount , sizeof(m_pointCount));
244 }
245 }
246
WritePointRecord(LASPoint const & point,const LASHeader & header)247 void WriterImpl::WritePointRecord(LASPoint const& point, const LASHeader& header)
248 {
249 // TODO: Static assert would be better
250
251 double t = 0;
252 assert(LASHeader::ePointSize0 == sizeof(m_record));
253 Writer::FillPointRecord(m_record, point, header);
254 detail::write_n(m_ofs, m_record, sizeof(m_record));
255
256 if (header.GetDataFormatId() == LASHeader::ePointFormat1) {
257 t = point.GetTime();
258 detail::write_n(m_ofs, t, sizeof(double));
259 }
260 ++m_pointCount;
261 }
262
263 }}} // namespace liblas::detail::v11
264
265