1 /******************************************************************************
2 * $Id$
3 *
4 * Project: libLAS - http://liblas.org - A BSD library for LAS format data.
5 * Purpose: LAS 1.0 reader 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 <liblas/liblas.hpp>
43 #include <liblas/detail/reader/reader.hpp>
44 #include <liblas/detail/reader/cachedreader.hpp>
45 #include <liblas/detail/private_utility.hpp>
46 // boost
47 #include <boost/cstdint.hpp>
48 // std
49 #include <fstream>
50 #include <istream>
51 #include <iostream>
52 #include <stdexcept>
53 #include <cstddef> // std::size_t
54 #include <cstdlib> // std::free
55 #include <cassert>
56
57 using namespace boost;
58
59 namespace liblas { namespace detail {
60
61
CachedReaderImpl(std::istream & ifs,std::size_t size)62 CachedReaderImpl::CachedReaderImpl(std::istream& ifs , std::size_t size)
63 : ReaderImpl(ifs)
64 , m_cache_size(size)
65 , m_cache_start_position(0)
66 , m_cache_read_position(0)
67 , m_cache_initialized(false)
68 {
69 }
70
ReadHeader()71 void CachedReaderImpl::ReadHeader()
72 {
73 ReaderImpl::ReadHeader();
74 HeaderPtr hptr(new liblas::Header(ReaderImpl::GetHeader()));
75
76 // If we were given no cache size, try to cache the whole thing
77 if (m_cache_size == 0) {
78 m_cache_size = hptr->GetPointRecordsCount();
79 }
80
81 if (m_cache_size > hptr->GetPointRecordsCount()) {
82 m_cache_size = hptr->GetPointRecordsCount();
83 }
84 // // FIXME: Note, vector::resize never shrinks the container and frees memory! Are we aware of this fact here? --mloskot
85 // m_cache.resize(m_cache_size);
86 //
87 // // Mark all positions as uncached and build up the mask
88 // // to the size of the number of points in the file
89 // uint8_t const uncached_mask = 0;
90 // cache_mask_type(hptr->GetPointRecordsCount(), uncached_mask).swap(m_mask);
91
92 m_header = hptr;
93 }
94
CacheData(uint32_t position)95 void CachedReaderImpl::CacheData(uint32_t position)
96 {
97 cache_mask_type::size_type old_cache_start_position = m_cache_start_position;
98 m_cache_start_position = position;
99
100 cache_mask_type::size_type header_size = static_cast<cache_mask_type::size_type>(m_header->GetPointRecordsCount());
101 cache_mask_type::size_type left_to_cache = (std::min)(m_cache_size, header_size - m_cache_start_position);
102
103 cache_mask_type::size_type to_mark = (std::min)(m_cache_size, header_size - old_cache_start_position);
104
105 for (uint32_t i = 0; i < to_mark; ++i)
106 {
107 m_mask[old_cache_start_position + i] = 0;
108 }
109
110 // if these aren't equal, we've hopped around with ReadPointAt
111 // and we need to seek to the proper position.
112 if (m_current != position) {
113 CachedReaderImpl::Seek(position);
114 m_current = position;
115 }
116 m_cache_read_position = position;
117
118 for (uint32_t i = 0; i < left_to_cache; ++i)
119 {
120 try {
121 m_mask[m_current] = 1;
122 ReadNextUncachedPoint();
123 m_cache[i] = new liblas::Point(ReaderImpl::GetPoint());
124 } catch (std::out_of_range&) {
125 // cached to the end
126 break;
127 }
128 }
129 }
130
ReadNextUncachedPoint()131 void CachedReaderImpl::ReadNextUncachedPoint()
132 {
133 if (0 == m_current)
134 {
135 m_ifs.clear();
136 m_ifs.seekg(m_header->GetDataOffset(), std::ios::beg);
137 }
138
139 if (m_current >= m_size ){
140 throw std::out_of_range("ReadNextPoint: file has no more points to read, end of file reached");
141 }
142
143 // m_point_reader->read();
144 detail::read_n(m_point->GetData().front(), m_ifs, m_record_size);
145 ++m_current;
146 // *m_point = m_point_reader->GetPoint();
147
148
149 if (!m_transforms.empty())
150 {
151 ReaderImpl::TransformPoint(*m_point);
152 }
153
154 }
155
ReadCachedPoint(uint32_t position)156 void CachedReaderImpl::ReadCachedPoint(uint32_t position) {
157
158 int32_t cache_position = position - m_cache_start_position ;
159
160 // std::cout << "MASK: ";
161 // std::vector<bool>::iterator it;
162 // for (it = m_mask.begin(); it != m_mask.end(); ++it) {
163 // std::cout << *it << ",";
164 // }
165 // std::cout << std::endl;
166
167 // If our point cache and mask have not yet been initialized, we
168 // should do so before tyring to read any points. We don't want to do
169 // this in ::Reset or ::ReadHeader, as these functions may be called
170 // multiple times and screw up our assumptions.
171 if (!m_cache_initialized)
172 {
173 m_cache = cache_type(m_cache_size);
174
175 // Mark all positions as uncached and build up the mask
176 // to the size of the number of points in the file
177 uint8_t const uncached_mask = 0;
178 cache_mask_type(m_header->GetPointRecordsCount(), uncached_mask).swap(m_mask);
179
180 m_cache_initialized = true;
181 }
182 if (m_mask[position] == 1) {
183 m_cache_read_position = position;
184 *m_point = *m_cache[cache_position];
185 return;
186 } else {
187
188 CacheData(position);
189
190 // At this point, we can't have a negative cache position.
191 // If we do, it's a big error or we'll segfault.
192 cache_position = position - m_cache_start_position ;
193 if (cache_position < 0) {
194 std::ostringstream msg;
195 msg << "ReadCachedPoint:: cache position: "
196 << cache_position
197 << " is negative. position or m_cache_start_position is invalid "
198 << "position: " << position << " m_cache_start_position: "
199 << m_cache_start_position;
200 throw std::runtime_error(msg.str());
201 }
202
203 if (m_mask[position] == 1) {
204 if (static_cast<uint32_t>(cache_position) > m_cache.size()) {
205 std::ostringstream msg;
206 msg << "ReadCachedPoint:: cache position: "
207 << position
208 << " greater than cache size: " << m_cache.size() ;
209 throw std::runtime_error(msg.str());
210 }
211 *m_point = *m_cache[cache_position];
212 return;
213 } else {
214 std::ostringstream msg;
215 msg << "ReadCachedPoint:: unable to obtain cached point"
216 << " at position: " << position
217 << " cache_position was " << cache_position ;
218 std::string out(msg.str());
219
220 throw std::runtime_error(out);
221 }
222 }
223 }
224
ReadNextPoint()225 void CachedReaderImpl::ReadNextPoint()
226 {
227 if (m_cache_read_position == m_size )
228 {
229 throw std::out_of_range("file has no more points to read, end of file reached");
230 }
231
232 // PointPtr ptr = ReadCachedPoint(m_cache_read_position, header);
233 ReadCachedPoint(m_cache_read_position);
234 ++m_cache_read_position;
235
236 // Filter the points and continue reading until we either find
237 // one to keep or throw an exception.
238
239 bool bLastPoint = false;
240 if (!FilterPoint(*m_point))
241 {
242 ReadCachedPoint(m_cache_read_position);
243 ++m_cache_read_position;
244
245 while (!FilterPoint(*m_point))
246 {
247 ReadCachedPoint(m_cache_read_position);
248 ++m_cache_read_position;
249 if (m_current == m_size)
250 {
251 bLastPoint = true;
252 break;
253 }
254 }
255 }
256
257 if (bLastPoint)
258 throw std::out_of_range("ReadNextPoint: file has no more points to read, end of file reached");
259
260 }
261
ReadPointAt(std::size_t n)262 liblas::Point const& CachedReaderImpl::ReadPointAt(std::size_t n)
263 {
264
265 if (n >= m_size ){
266 throw std::out_of_range("file has no more points to read, end of file reached");
267
268 } else if (m_size < n) {
269 std::ostringstream output;
270 output << "ReadPointAt:: Inputted value: "
271 << n << " is greater than the number of points: "
272 << m_size;
273 std::string out(output.str());
274 throw std::runtime_error(out);
275 }
276
277 ReadCachedPoint(n);
278 m_cache_read_position = n;
279 return *m_point;
280 }
281
Reset()282 void CachedReaderImpl::Reset()
283 {
284 if (m_mask.empty())
285 {
286 ReaderImpl::Reset();
287 return;
288 }
289
290 typedef cache_mask_type::size_type size_type;
291 size_type old_cache_start_position = m_cache_start_position;
292 size_type header_size = static_cast<size_type>(m_header->GetPointRecordsCount());
293 size_type to_mark = (std::min)(m_cache_size, header_size - old_cache_start_position);
294
295 for (uint32_t i = 0; i < to_mark; ++i) {
296
297 size_type const mark_pos = m_cache_start_position + i;
298 assert(mark_pos < m_mask.size());
299
300 m_mask[mark_pos] = 0;
301 }
302
303 m_cache_start_position = 0;
304 m_cache_read_position = 0;
305 m_cache_initialized = false;
306
307 ReaderImpl::Reset();
308 }
309
Seek(std::size_t n)310 void CachedReaderImpl::Seek(std::size_t n)
311 {
312
313 if (n < 1) {
314 CachedReaderImpl::Reset();
315 }
316
317 m_cache_read_position = n;
318 ReaderImpl::Seek(n);
319 }
320
SetFilters(std::vector<liblas::FilterPtr> const & filters)321 void CachedReaderImpl::SetFilters(std::vector<liblas::FilterPtr> const& filters)
322 {
323 Reset();
324 ReaderImpl::SetFilters(filters);
325 }
GetFilters() const326 std::vector<liblas::FilterPtr> CachedReaderImpl::GetFilters() const
327 {
328 return ReaderImpl::GetFilters();
329 }
330
SetTransforms(std::vector<liblas::TransformPtr> const & transforms)331 void CachedReaderImpl::SetTransforms(std::vector<liblas::TransformPtr> const& transforms)
332 {
333 // Setting transforms means the cache is invalid
334 Reset();
335 ReaderImpl::SetTransforms(transforms);
336
337 }
338
GetTransforms() const339 std::vector<liblas::TransformPtr> CachedReaderImpl::GetTransforms() const
340 {
341 return ReaderImpl::GetTransforms();
342 }
343 }} // namespace liblas::detail
344
345