1 /******************************************************************************
2 * $Id$
3 *
4 * Project: libLAS - http://liblas.org - A BSD library for LAS format data.
5 * Purpose: LAS index class
6 * Author: Gary Huber, gary@garyhuberart.com
7 *
8 ******************************************************************************
9 * Copyright (c) 2010, Gary Huber, gary@garyhuberart.com
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/index.hpp>
43 #include <liblas/writer.hpp>
44 #include <liblas/detail/index/indexoutput.hpp>
45 #include <liblas/detail/index/indexcell.hpp>
46 #include <liblas/detail/writer/writer.hpp>
47
48 namespace liblas
49 {
50
Index()51 Index::Index()
52 {
53 SetValues();
54 m_indexBuilt = false;
55 } // Index::Index
56
Index(IndexData const & ParamSrc)57 Index::Index(IndexData const& ParamSrc)
58 {
59 SetValues();
60 Prep(ParamSrc);
61 } // Index::Index
62
Prep(IndexData const & ParamSrc)63 bool Index::Prep(IndexData const& ParamSrc)
64 {
65
66 m_reader = ParamSrc.m_reader;
67 m_idxreader = ParamSrc.m_idxreader;
68 m_readerCreated = false;
69 if (! m_reader)
70 {
71 try {
72 // fails if input stream is invalid
73 m_reader = new liblas::Reader(*ParamSrc.m_ifs);
74 m_readerCreated = true;
75 } // try
76 catch (std::runtime_error) {
77 return (InputFileError("Index::Prep"));
78 } // catch
79 } // if
80 m_ofs = ParamSrc.m_ofs;
81 m_debugOutputLevel = ParamSrc.m_debugOutputLevel;
82 m_tempFileName = ParamSrc.m_tempFileName ? ParamSrc.m_tempFileName: "";
83 m_indexAuthor = ParamSrc.m_indexAuthor ? ParamSrc.m_indexAuthor: "";
84 m_indexComment = ParamSrc.m_indexComment ? ParamSrc.m_indexComment: "";
85 m_indexDate = ParamSrc.m_indexDate ? ParamSrc.m_indexDate: "";
86 m_cellSizeZ = ParamSrc.m_cellSizeZ;
87 m_debugger = ParamSrc.m_debugger ? ParamSrc.m_debugger: stderr;
88 m_readOnly = ParamSrc.m_readOnly;
89 m_writestandaloneindex = ParamSrc.m_writestandaloneindex;
90 m_forceNewIndex = ParamSrc.m_forceNewIndex;
91 if (ParamSrc.m_maxMemoryUsage > 0)
92 m_maxMemoryUsage = ParamSrc.m_maxMemoryUsage;
93 else
94 m_maxMemoryUsage = LIBLAS_INDEX_MAXMEMDEFAULT;
95 if (m_maxMemoryUsage < LIBLAS_INDEX_MINMEMDEFAULT)
96 m_maxMemoryUsage = LIBLAS_INDEX_MINMEMDEFAULT;
97 m_indexBuilt = IndexInit();
98 return (m_indexBuilt);
99
100 } // Index::Prep
101
SetValues(void)102 void Index::SetValues(void)
103 {
104 m_bounds.dimension(3);
105 m_reader = 0;
106 m_idxreader = 0;
107 m_ofs = 0;
108 m_readerCreated = false;
109 m_tempFile = 0;
110 m_outputFile = 0;
111 m_debugOutputLevel = 0;
112 m_tempFileName = "";
113 m_indexAuthor = "";
114 m_indexComment = "";
115 m_indexDate = "";
116 m_versionMajor = LIBLAS_INDEX_VERSIONMAJOR;
117 m_versionMinor = LIBLAS_INDEX_VERSIONMINOR;
118 m_cellSizeZ = 0.0;
119 m_debugger = stderr;
120 m_readOnly = false;
121 m_forceNewIndex = false;
122 m_DataVLR_ID = 43;
123 m_maxMemoryUsage = LIBLAS_INDEX_MAXMEMDEFAULT;
124 m_rangeX = m_rangeY = m_rangeZ = m_cellSizeZ = m_cellSizeX = m_cellSizeY =
125 m_pointRecordsCount = m_maxMemoryUsage = m_cellsX = m_cellsY = m_cellsZ = m_totalCells = 0;
126 m_tempFileWrittenBytes = 0;
127 m_indexBuilt = m_tempFileStarted = m_readerCreated = false;
128 } // Index::SetValues
129
~Index(void)130 Index::~Index(void)
131 {
132 if (m_readerCreated)
133 delete m_reader;
134 } // Index::~Index
135
IndexInit(void)136 bool Index::IndexInit(void)
137 {
138 bool Success = false;
139 bool IndexFound = false;
140
141 if (m_idxreader || m_reader)
142 {
143 if (m_idxreader)
144 {
145 m_idxheader = m_idxreader->GetHeader();
146 } // if
147 else if (m_reader)
148 {
149 m_idxheader = m_reader->GetHeader();
150 } // else
151 if (m_reader)
152 {
153 m_pointheader = m_reader->GetHeader();
154 } // else
155 uint32_t initialVLRs = m_idxheader.GetRecordsCount();
156 for (uint32_t i = 0; i < initialVLRs; ++i)
157 {
158 VariableRecord const& vlr = m_idxheader.GetVLR(i);
159 // a combination of "liblas" and 42 denotes that this is a liblas spatial index id
160 if (std::string(vlr.GetUserId(false)) == std::string("liblas"))
161 {
162 if (vlr.GetRecordId() == 42)
163 {
164 LoadIndexVLR(vlr);
165 IndexFound = true;
166 break;
167 }
168 }
169 }
170 if (IndexFound)
171 {
172 if (m_forceNewIndex)
173 {
174 ClearOldIndex();
175 IndexFound = false;
176 if (m_debugOutputLevel > 1)
177 fprintf(m_debugger, "Old index removed.\n");
178 } // if
179 else if (! Validate())
180 {
181 IndexFound = false;
182 if (m_debugOutputLevel > 1)
183 fprintf(m_debugger, "Existing index out of date.\n");
184 } // else if failed index validation test
185 else
186 return (true);
187 } // if
188 if (! IndexFound)
189 {
190 if (! m_readOnly)
191 {
192 Success = BuildIndex();
193 uint32_t test = m_idxheader.GetRecordsCount() - initialVLRs;
194 if (m_debugOutputLevel > 1)
195 fprintf(m_debugger, "VLRs created %d\n", test);
196 } // if
197 else if (m_debugOutputLevel > 1)
198 fprintf(m_debugger, "Index not found nor created per user instructions.\n");
199 } // if
200 return Success;
201 } // m_reader
202 return (InitError("Index::IndexInit"));
203 } // Index::IndexInit
204
ClearOldIndex(void)205 void Index::ClearOldIndex(void)
206 {
207 uint32_t initialVLRs = m_idxheader.GetRecordsCount();
208 uint32_t TempDataVLR_ID = GetDataVLR_ID();
209
210 for (uint32_t i = 0; i < initialVLRs; ++i)
211 {
212 VariableRecord const& vlr = m_idxheader.GetVLR(i);
213 // a combination of "liblas" and 42 denotes that this is a liblas spatial index id
214 if (std::string(vlr.GetUserId(false)) == std::string("liblas"))
215 {
216 // 42 is index ID
217 if (vlr.GetRecordId() == 42)
218 {
219 // sets DataVLR_ID to value in index header
220 LoadIndexVLR(vlr);
221 m_idxheader.DeleteVLR(i);
222 } // if
223 else if (vlr.GetRecordId() == GetDataVLR_ID())
224 {
225 m_idxheader.DeleteVLR(i);
226 } // else if
227 } // if
228 } // for
229
230 // restore Data VLR ID
231 SetDataVLR_ID(TempDataVLR_ID);
232
233 } // Index::ClearOldIndex
234
Validate(void)235 bool Index::Validate(void)
236 {
237
238 // compare the index-stored values to the ones in the las file header
239 // Index needs to be current in order to be valid.
240 // The validity test cannot determine if points have been moved but if the number of points or the
241 // bounds of the points has changed since the index was built then the index is deemed invalid and
242 // not to be used for filtering.
243 Bounds<double> HeaderBounds(m_pointheader.GetMinX(), m_pointheader.GetMinY(), m_pointheader.GetMinZ(), m_pointheader.GetMaxX(), m_pointheader.GetMaxY(), m_pointheader.GetMaxZ());
244 if (m_bounds == HeaderBounds)
245 {
246 if (m_pointheader.GetPointRecordsCount() == GetPointRecordsCount())
247 return (true);
248 } // if
249 return (false);
250
251 } // Index::Validate
252
GetDefaultReserve(void)253 uint32_t Index::GetDefaultReserve(void)
254 {
255 return (GetPointRecordsCount() < LIBLAS_INDEX_RESERVEFILTERDEFAULT ? GetPointRecordsCount(): LIBLAS_INDEX_RESERVEFILTERDEFAULT);
256 } // Index::GetDefaultReserve
257
Filter(IndexData & ParamSrc)258 const std::vector<uint32_t>& Index::Filter(IndexData & ParamSrc)
259 {
260
261 try {
262 // if there is already a list, get rid of it
263 m_filterResult.resize(0);
264 // are we asked to advance beyond the number of points in the file? that would be a mistake and waste of time.
265 if (ParamSrc.m_iterator &&
266 (ParamSrc.m_iterator->m_advance + ParamSrc.m_iterator->m_totalPointsScanned > GetPointRecordsCount()))
267 return (m_filterResult);
268
269 m_filterResult.reserve(ParamSrc.m_iterator ? ParamSrc.m_iterator->m_chunkSize: GetDefaultReserve());
270 if (m_reader)
271 {
272 uint32_t i;
273 i = ParamSrc.m_iterator ? ParamSrc.m_iterator->m_curVLR: 0;
274 for (; i < m_idxheader.GetRecordsCount(); ++i)
275 {
276 VariableRecord const& vlr = m_idxheader.GetVLR(i);
277 // a combination of "liblas" and 42 denotes that this is a liblas spatial index id
278 if (std::string(vlr.GetUserId(false)) == std::string("liblas"))
279 {
280 uint16_t RecordID = vlr.GetRecordId();
281 if (RecordID == 42)
282 {
283 if (! LoadIndexVLR(vlr))
284 break;
285 // Beyond the first record would be more VLR's with the actual index data
286 // some cells will fall completely inside, some outside and some straddling the filter bounds
287 SetCellFilterBounds(ParamSrc);
288 if (! m_bounds.intersects(ParamSrc.m_filter))
289 {
290 if (m_debugOutputLevel > 1)
291 fprintf(m_debugger, "Index bounds do not intersect filter bounds.\n");
292 break;
293 } // if
294 if (ParamSrc.m_iterator && ! ParamSrc.m_iterator->ValidateIndexVersion(GetVersionMajor(), GetVersionMinor()))
295 {
296 if (m_debugOutputLevel > 1)
297 fprintf(m_debugger, "Index version does not support iterator access. Regenerate Index.\n");
298 break;
299 } // if
300 } // if 42
301 else if (RecordID == m_DataVLR_ID)
302 {
303 // some of our data is in this record
304 bool VLRDone = false;
305 const uint32_t HeadVLR = i;
306 if (! FilterOneVLR(vlr, i, ParamSrc, VLRDone))
307 break;
308 if (ParamSrc.m_iterator)
309 {
310 if (VLRDone)
311 ParamSrc.m_iterator->m_curCellStartPos = ParamSrc.m_iterator->m_ptsScannedCurCell =
312 ParamSrc.m_iterator->m_ptsScannedCurVLR = 0;
313 // if we've filled our quota break out of loop
314 if (m_filterResult.size() >= ParamSrc.m_iterator->m_chunkSize)
315 {
316 // if we've scanned the entire VLR
317 if (VLRDone)
318 ++i; // increment i so that next iteration starts on next VLR
319 else if (i != HeadVLR) // if VLR's are linked FilterOneVLR() will have incremented i
320 i = HeadVLR; // put back i so that the right VLR is loaded first next iteration
321 break;
322 } // if
323 } // if
324 } // else if ID matches ID stored in index header
325 }
326 }
327 if (ParamSrc.m_iterator)
328 ParamSrc.m_iterator->m_curVLR = i;
329 } // m_reader
330 } // try
331 catch (std::bad_alloc) {
332 m_filterResult.resize(0);
333 } // catch
334 return (m_filterResult);
335
336 } // Index::Filter
337
Filter(IndexData const & ParamSrc,uint32_t ChunkSize)338 IndexIterator* Index::Filter(IndexData const& ParamSrc, uint32_t ChunkSize)
339 {
340 IndexIterator* NewIter = NULL;
341
342 try {
343 NewIter = new IndexIterator(this, ParamSrc, ChunkSize);
344 } // try
345 catch (std::bad_alloc) {
346 return (NULL);
347 } // catch
348
349 return (NewIter);
350
351 } // Index::Filter
352
Filter(double LowFilterX,double HighFilterX,double LowFilterY,double HighFilterY,double LowFilterZ,double HighFilterZ,uint32_t ChunkSize)353 IndexIterator* Index::Filter(double LowFilterX, double HighFilterX, double LowFilterY, double HighFilterY,
354 double LowFilterZ, double HighFilterZ, uint32_t ChunkSize)
355 {
356 IndexIterator* NewIter = NULL;
357
358 try {
359 NewIter = new IndexIterator(this, LowFilterX, HighFilterX, LowFilterY, HighFilterY,
360 LowFilterZ, HighFilterZ, ChunkSize);
361 } // try
362 catch (std::bad_alloc) {
363 return (NULL);
364 } // catch
365
366 return (NewIter);
367
368 } // Index::Filter
369
Filter(Bounds<double> const & BoundsSrc,uint32_t ChunkSize)370 IndexIterator* Index::Filter(Bounds<double> const& BoundsSrc, uint32_t ChunkSize)
371 {
372 IndexIterator* NewIter = NULL;
373
374 try {
375 NewIter = new IndexIterator(this, BoundsSrc, ChunkSize);
376 } // try
377 catch (std::bad_alloc) {
378 return (NULL);
379 } // catch
380
381 return (NewIter);
382
383 } // Index::Filter
384
SetCellFilterBounds(IndexData & ParamSrc)385 void Index::SetCellFilterBounds(IndexData & ParamSrc)
386 {
387 double LowXCell, HighXCell, LowYCell, HighYCell, LowZCell, HighZCell,
388 filterMinXCell, filterMaxXCell, filterMinYCell, filterMaxYCell, filterMinZCell, filterMaxZCell;
389 // convert filter bounds into cell numbers
390 // X and Y range can not be 0 or the index would not have been built
391 filterMinXCell = m_cellsX * (ParamSrc.GetMinFilterX() - GetMinX()) / GetRangeX();
392 filterMaxXCell = m_cellsX * (ParamSrc.GetMaxFilterX() - GetMinX()) / GetRangeX();
393 filterMinYCell = m_cellsY * (ParamSrc.GetMinFilterY() - GetMinY()) / GetRangeY();
394 filterMaxYCell = m_cellsY * (ParamSrc.GetMaxFilterY() - GetMinY()) / GetRangeY();
395 // Z range however can be 0
396 if (GetRangeZ() > 0.0 && ! detail::compare_distance(GetRangeZ(), 0.0))
397 {
398 filterMinZCell = m_cellsZ * (ParamSrc.GetMinFilterZ() - GetMinZ()) / GetRangeZ();
399 filterMaxZCell = m_cellsZ * (ParamSrc.GetMaxFilterZ() - GetMinZ()) / GetRangeZ();
400 } // if
401 else
402 {
403 filterMinZCell = filterMaxZCell = 0;
404 } // else
405 LowXCell = ceil(filterMinXCell);
406 HighXCell = floor(filterMaxXCell) - 1.0;
407 LowYCell = ceil(filterMinYCell);
408 HighYCell = floor(filterMaxYCell) - 1.0;
409 LowZCell = ceil(filterMinZCell);
410 HighZCell = floor(filterMaxZCell) - 1.0;
411 ParamSrc.m_LowXCellCompletelyIn = (int32_t)LowXCell;
412 ParamSrc.m_HighXCellCompletelyIn = (int32_t)HighXCell;
413 ParamSrc.m_LowYCellCompletelyIn = (int32_t)LowYCell;
414 ParamSrc.m_HighYCellCompletelyIn = (int32_t)HighYCell;
415 ParamSrc.m_LowZCellCompletelyIn = (int32_t)LowZCell;
416 ParamSrc.m_HighZCellCompletelyIn = (int32_t)HighZCell;
417
418 LowXCell = floor(filterMinXCell);
419 HighXCell = ceil(filterMaxXCell) - 1.0;
420 LowYCell = floor(filterMinYCell);
421 HighYCell = ceil(filterMaxYCell) - 1.0;
422 LowZCell = floor(filterMinZCell);
423 HighZCell = ceil(filterMaxZCell) - 1.0;
424 ParamSrc.m_LowXBorderCell = (int32_t)LowXCell;
425 ParamSrc.m_HighXBorderCell = (int32_t)HighXCell;
426 ParamSrc.m_LowYBorderCell = (int32_t)LowYCell;
427 ParamSrc.m_HighYBorderCell = (int32_t)HighYCell;
428 ParamSrc.m_LowZBorderCell = (int32_t)LowZCell;
429 ParamSrc.m_HighZBorderCell = (int32_t)HighZCell;
430
431 ParamSrc.m_LowXBorderPartCell = filterMinXCell - LowXCell;
432 ParamSrc.m_HighXBorderPartCell = filterMaxXCell - HighXCell;
433 ParamSrc.m_LowYBorderPartCell = filterMinYCell - LowYCell;
434 ParamSrc.m_HighYBorderPartCell = filterMaxYCell - HighYCell;
435
436 } // Index::SetCellFilterBounds
437
LoadIndexVLR(VariableRecord const & vlr)438 bool Index::LoadIndexVLR(VariableRecord const& vlr)
439 {
440 char DestStr[512];
441 uint16_t StringLen;
442 uint16_t ReadPos = 0;
443
444 try {
445 //uint16_t VLRIndexRecLen = vlr.GetRecordLength();
446 // GetData returns a vector of uint8_t
447 // std::vector<uint8_t>
448 // read the first record of our index data
449 IndexVLRData const& VLRIndexData = vlr.GetData();
450 // parse the index header data
451 ReadVLRData_n(m_versionMajor, VLRIndexData, ReadPos);
452 ReadVLRData_n(m_versionMinor, VLRIndexData, ReadPos);
453 // creator
454 ReadVLRData_n(StringLen, VLRIndexData, ReadPos);
455 ReadeVLRData_str(DestStr, VLRIndexData, StringLen, ReadPos);
456 SetIndexAuthorStr(DestStr);
457
458 // comment
459 ReadVLRData_n(StringLen, VLRIndexData, ReadPos);
460 ReadeVLRData_str(DestStr, VLRIndexData, StringLen, ReadPos);
461 SetIndexCommentStr(DestStr);
462
463 // date
464 ReadVLRData_n(StringLen, VLRIndexData, ReadPos);
465 ReadeVLRData_str(DestStr, VLRIndexData, StringLen, ReadPos);
466 SetIndexDateStr(DestStr);
467
468 // file index extents
469 double TempData;
470 ReadVLRData_n(TempData, VLRIndexData, ReadPos);
471 SetMinX(TempData);
472 ReadVLRData_n(TempData, VLRIndexData, ReadPos);
473 SetMaxX(TempData);
474 ReadVLRData_n(TempData, VLRIndexData, ReadPos);
475 SetMinY(TempData);
476 ReadVLRData_n(TempData, VLRIndexData, ReadPos);
477 SetMaxY(TempData);
478 ReadVLRData_n(TempData, VLRIndexData, ReadPos);
479 SetMinZ(TempData);
480 ReadVLRData_n(TempData, VLRIndexData, ReadPos);
481 SetMaxZ(TempData);
482
483 // ID number of associated data VLR's - normally 43 but may use heigher numbers
484 // in order to store more than one index in a file
485 uint32_t TempLong;
486 ReadVLRData_n(TempLong, VLRIndexData, ReadPos);
487 SetDataVLR_ID(TempLong);
488
489 // number of points indexed and cells in the index
490 ReadVLRData_n(TempLong, VLRIndexData, ReadPos);
491 SetPointRecordsCount(TempLong);
492 ReadVLRData_n(TempLong, VLRIndexData, ReadPos);
493 SetCellsX(TempLong);
494 ReadVLRData_n(TempLong, VLRIndexData, ReadPos);
495 SetCellsY(TempLong);
496 ReadVLRData_n(TempLong, VLRIndexData, ReadPos);
497 SetCellsZ(TempLong);
498
499 CalcRangeX();
500 CalcRangeY();
501 CalcRangeZ();
502 } // try
503 catch (std::bad_alloc) {
504 return (false);
505 } // catch
506 catch (std::out_of_range) {
507 return (false);
508 } // catch
509 return true;
510
511 } // Index::LoadIndexVLR
512
FilterOneVLR(VariableRecord const & vlr,uint32_t & i,IndexData & ParamSrc,bool & VLRDone)513 bool Index::FilterOneVLR(VariableRecord const& vlr, uint32_t& i, IndexData & ParamSrc, bool & VLRDone)
514 {
515
516 uint32_t ReadPos = 0;
517 uint32_t MinCellX, MinCellY, MaxCellX, MaxCellY, PointsThisRecord = 0, PointsThisCell = 0, DataRecordSize = 0,
518 PointsScannedThisTime = 0, PointsScannedCurVLR = 0, PointsToIgnore = 0;
519 IndexVLRData CompositeData;
520
521 try {
522 IndexVLRData const& VLRIndexRecordData = vlr.GetData();
523 uint16_t VLRIndexRecLen = vlr.GetRecordLength();
524 CompositeData.resize(VLRIndexRecLen);
525 ReadVLRDataNoInc_str((char *)&CompositeData[0], VLRIndexRecordData, VLRIndexRecLen, 0);
526
527 ReadVLRData_n(MinCellX, CompositeData, ReadPos);
528 ReadVLRData_n(MinCellY, CompositeData, ReadPos);
529 // last cell in VLR, x, y
530 ReadVLRData_n(MaxCellX, CompositeData, ReadPos);
531 ReadVLRData_n(MaxCellY, CompositeData, ReadPos);
532 // data record size
533 ReadVLRData_n(DataRecordSize, CompositeData, ReadPos);
534 // number of points in this VLR - added in Index version 1.1
535 if (m_versionMajor > 1 || m_versionMinor >= 1)
536 ReadVLRData_n(PointsThisRecord, CompositeData, ReadPos);
537
538 if (DataRecordSize > VLRIndexRecLen)
539 {
540 CompositeData.resize(DataRecordSize);
541 // read more records and concatenate data
542 uint32_t ReadData = VLRIndexRecLen;
543 uint32_t UnreadData = DataRecordSize - ReadData;
544 while (UnreadData)
545 {
546 ++i;
547 VariableRecord const& vlr2 = m_idxheader.GetVLR(i);
548 IndexVLRData const& TempData = vlr2.GetData();
549 uint16_t TempRecLen = vlr2.GetRecordLength();
550 ReadVLRDataNoInc_str((char *)&CompositeData[ReadData], TempData, TempRecLen, 0);
551 ReadData += TempRecLen;
552 if (UnreadData >= TempRecLen)
553 UnreadData -= TempRecLen;
554 else
555 // this is an error if we get here
556 UnreadData = 0;
557 } // while
558 } // if
559
560 if (VLRInteresting(MinCellX, MinCellY, MaxCellX, MaxCellY, ParamSrc))
561 {
562 // if using iterator, jump to start of last cell that was being scanned
563 if (ParamSrc.m_iterator && (ParamSrc.m_iterator->m_curCellStartPos >= ReadPos))
564 {
565 ReadPos = ParamSrc.m_iterator->m_curCellStartPos;
566 PointsToIgnore = ParamSrc.m_iterator->m_ptsScannedCurCell;
567 PointsScannedCurVLR = ParamSrc.m_iterator->m_ptsScannedCurVLR;
568 } // if
569 // translate the data for this VLR
570 while (ReadPos + sizeof (uint32_t) < DataRecordSize)
571 {
572 if (ParamSrc.m_iterator)
573 {
574 ParamSrc.m_iterator->m_curCellStartPos = ReadPos;
575 ParamSrc.m_iterator->m_ptsScannedCurCell = 0;
576 } // if
577 // current cell, x, y
578 uint32_t x, y, PtRecords, SubCellsXY, SubCellsZ;
579 ReadVLRData_n(x, CompositeData, ReadPos);
580 ReadVLRData_n(y, CompositeData, ReadPos);
581 if (ParamSrc.m_iterator)
582 {
583 ParamSrc.m_iterator->m_curCellX = x;
584 ParamSrc.m_iterator->m_curCellY = y;
585 } // if
586 // number of points in this cell - added in Index version 1.1
587 if (m_versionMajor > 1 || m_versionMinor >= 1)
588 ReadVLRData_n(PointsThisCell, CompositeData, ReadPos);
589
590 bool TestPointsInThisCell = CellInteresting(x, y, ParamSrc);
591 // min and max Z
592 liblas::detail::ElevExtrema CellMinZ, CellMaxZ;
593 ReadVLRData_n(CellMinZ, CompositeData, ReadPos);
594 ReadVLRData_n(CellMaxZ, CompositeData, ReadPos);
595 // number of subcells in this cell in both XY and Z
596 ReadVLRData_n(PtRecords, CompositeData, ReadPos);
597 ReadVLRData_n(SubCellsXY, CompositeData, ReadPos);
598 ReadVLRData_n(SubCellsZ, CompositeData, ReadPos);
599
600 // read the data stored in Z cells, if any
601 for (uint32_t SubCellZ = 0; SubCellZ < SubCellsZ; ++SubCellZ)
602 {
603 uint32_t ZCellID;
604 ReadVLRData_n(ZCellID, CompositeData, ReadPos);
605 // number of point records in subcell
606 uint32_t ZCellNumRecords;
607 ReadVLRData_n(ZCellNumRecords, CompositeData, ReadPos);
608 for (uint32_t SubCellZPt = 0; SubCellZPt < ZCellNumRecords; ++SubCellZPt)
609 {
610 uint32_t PointID;
611 ReadVLRData_n(PointID, CompositeData, ReadPos);
612 assert(PointID < m_pointRecordsCount);
613 liblas::detail::ConsecPtAccumulator ConsecutivePts;
614 ReadVLRData_n(ConsecutivePts, CompositeData, ReadPos);
615 if (TestPointsInThisCell && ZCellInteresting(ZCellID, ParamSrc))
616 {
617 FilterPointSeries(PointID, PointsScannedThisTime, PointsToIgnore, x, y, ZCellID,
618 ConsecutivePts, ParamSrc.m_iterator, ParamSrc);
619 } // if
620 else
621 {
622 PointsScannedThisTime += ConsecutivePts;
623 if (ParamSrc.m_iterator)
624 ParamSrc.m_iterator->m_ptsScannedCurCell += ConsecutivePts;
625 } // else
626 if (ParamSrc.m_iterator && (m_filterResult.size() >= ParamSrc.m_iterator->m_chunkSize))
627 break;
628 } // for
629 if (ParamSrc.m_iterator && (m_filterResult.size() >= ParamSrc.m_iterator->m_chunkSize))
630 break;
631 } // for
632 // read the data stored in XY quadtree cells
633 for (uint32_t SubCellXY = 0; SubCellXY < SubCellsXY; ++SubCellXY)
634 {
635 uint32_t SubCellID;
636 ReadVLRData_n(SubCellID, CompositeData, ReadPos);
637 // number of point records in subcell
638 uint32_t SubCellNumRecords;
639 ReadVLRData_n(SubCellNumRecords, CompositeData, ReadPos);
640 for (uint32_t SubCellPt = 0; SubCellPt < SubCellNumRecords; ++SubCellPt)
641 {
642 uint32_t PointID;
643 ReadVLRData_n(PointID, CompositeData, ReadPos);
644 assert(PointID < m_pointRecordsCount);
645 liblas::detail::ConsecPtAccumulator ConsecutivePts;
646 ReadVLRData_n(ConsecutivePts, CompositeData, ReadPos);
647 if (TestPointsInThisCell && SubCellInteresting(SubCellID, x, y, ParamSrc))
648 {
649 FilterPointSeries(PointID, PointsScannedThisTime, PointsToIgnore, x, y, 0,
650 ConsecutivePts, ParamSrc.m_iterator, ParamSrc);
651 } // if
652 else
653 {
654 PointsScannedThisTime += ConsecutivePts;
655 if (ParamSrc.m_iterator)
656 ParamSrc.m_iterator->m_ptsScannedCurCell += ConsecutivePts;
657 } // else
658 if (ParamSrc.m_iterator && (m_filterResult.size() >= ParamSrc.m_iterator->m_chunkSize))
659 break;
660 } // for
661 if (ParamSrc.m_iterator && (m_filterResult.size() >= ParamSrc.m_iterator->m_chunkSize))
662 break;
663 } // for
664 // read data in unsubdivided cells
665 if (! (SubCellsZ || SubCellsXY))
666 {
667 for (uint32_t CurPt = 0; CurPt < PtRecords; ++CurPt)
668 {
669 uint32_t PointID;
670 ReadVLRData_n(PointID, CompositeData, ReadPos);
671 assert(PointID < m_pointRecordsCount);
672 liblas::detail::ConsecPtAccumulator ConsecutivePts;
673 ReadVLRData_n(ConsecutivePts, CompositeData, ReadPos);
674 if (TestPointsInThisCell)
675 {
676 FilterPointSeries(PointID, PointsScannedThisTime, PointsToIgnore, x, y, 0,
677 ConsecutivePts, ParamSrc.m_iterator, ParamSrc);
678 } // if
679 else
680 {
681 PointsScannedThisTime += ConsecutivePts;
682 if (ParamSrc.m_iterator)
683 ParamSrc.m_iterator->m_ptsScannedCurCell += ConsecutivePts;
684 } // else
685 if (ParamSrc.m_iterator && (m_filterResult.size() >= ParamSrc.m_iterator->m_chunkSize))
686 break;
687 } // for
688 } // if
689 if (ParamSrc.m_iterator && (m_filterResult.size() >= ParamSrc.m_iterator->m_chunkSize))
690 break;
691 } // while
692 if (PointsScannedThisTime >= PointsToIgnore)
693 {
694 PointsScannedCurVLR += PointsScannedThisTime - PointsToIgnore;
695 if (PointsScannedCurVLR >= PointsThisRecord)
696 VLRDone = true;
697 if (ParamSrc.m_iterator)
698 {
699 ParamSrc.m_iterator->m_totalPointsScanned += PointsScannedThisTime - PointsToIgnore;
700 ParamSrc.m_iterator->m_ptsScannedCurVLR = PointsScannedCurVLR;
701 } // if
702 } // if
703 } // if
704 else if (ParamSrc.m_iterator)
705 {
706 ParamSrc.m_iterator->m_totalPointsScanned += PointsThisRecord;
707 VLRDone = true;
708 } // else if
709 // need to be more sophisticated but this is a test
710 } // try
711 catch (std::bad_alloc) {
712 return (false);
713 } // catch
714 catch (std::out_of_range) {
715 return (false);
716 } // catch
717 return true;
718
719 } // Index::FilterOneVLR
720
FilterPointSeries(uint32_t & PointID,uint32_t & PointsScanned,uint32_t const PointsToIgnore,uint32_t const x,uint32_t const y,uint32_t const z,liblas::detail::ConsecPtAccumulator const ConsecutivePts,IndexIterator * Iterator,IndexData const & ParamSrc)721 bool Index::FilterPointSeries(uint32_t & PointID, uint32_t & PointsScanned,
722 uint32_t const PointsToIgnore, uint32_t const x, uint32_t const y, uint32_t const z,
723 liblas::detail::ConsecPtAccumulator const ConsecutivePts, IndexIterator *Iterator,
724 IndexData const& ParamSrc)
725 {
726 bool LastPtRead = 0;
727 uint32_t LastPointID = static_cast<uint32_t>(~0);
728
729 try {
730 for (uint32_t PtCt = 0; PtCt < ConsecutivePts; ++PointID, ++PtCt)
731 {
732 ++PointsScanned;
733 if (Iterator)
734 ++Iterator->m_ptsScannedCurCell;
735 if (PointsScanned > PointsToIgnore)
736 {
737 if (FilterOnePoint(x, y, z, PointID, LastPointID, LastPtRead, ParamSrc))
738 {
739 bool SkipIt = false;
740 if (Iterator)
741 {
742 ++Iterator->m_conformingPtsFound;
743 if (Iterator->m_advance)
744 {
745 --Iterator->m_advance;
746 if (Iterator->m_advance)
747 SkipIt = true;
748 } // if
749 } // if
750 if (! SkipIt)
751 {
752 m_filterResult.push_back(PointID);
753 if (Iterator && (m_filterResult.size() >= Iterator->m_chunkSize))
754 break;
755 } // if
756 } // if
757 } // if
758 LastPointID = PointID;
759 } // for
760 return (true);
761 } // try
762 catch (std::bad_alloc) {
763 return (false);
764 } // catch
765
766 } // Index::FilterPointSeries
767
VLRInteresting(int32_t MinCellX,int32_t MinCellY,int32_t MaxCellX,int32_t MaxCellY,IndexData const & ParamSrc)768 bool Index::VLRInteresting(int32_t MinCellX, int32_t MinCellY, int32_t MaxCellX, int32_t MaxCellY, IndexData const& ParamSrc)
769 {
770
771 // cells are written in south to north cell order columns (x) and west to east rows (y)
772 // The writing loops through y inside a for x loop. Therefore if the low x and high x are not the same then
773 // we have to assume that all y cells are interesting
774 if (ParamSrc.m_noFilterX || (MaxCellX >= ParamSrc.m_LowXBorderCell && MinCellX <= ParamSrc.m_HighXBorderCell))
775 {
776 if (ParamSrc.m_noFilterY || (MaxCellX != MinCellX) ||
777 (MaxCellY >= ParamSrc.m_LowYBorderCell && MinCellY <= ParamSrc.m_HighYBorderCell))
778 return true;
779 } // if
780 return false;
781
782 } // Index::VLRInteresting
783
CellInteresting(int32_t XCellID,int32_t YCellID,IndexData const & ParamSrc)784 bool Index::CellInteresting(int32_t XCellID, int32_t YCellID, IndexData const& ParamSrc)
785 {
786
787 if (ParamSrc.m_noFilterX || (XCellID >= ParamSrc.m_LowXBorderCell && XCellID <= ParamSrc.m_HighXBorderCell))
788 {
789 if (ParamSrc.m_noFilterY || (YCellID >= ParamSrc.m_LowYBorderCell && YCellID <= ParamSrc.m_HighYBorderCell))
790 return true;
791 } // if
792 return false;
793
794 } // Index::CellInteresting
795
ZCellInteresting(int32_t ZCellID,IndexData const & ParamSrc)796 bool Index::ZCellInteresting(int32_t ZCellID, IndexData const& ParamSrc)
797 {
798
799 if (ParamSrc.m_noFilterZ || (ZCellID >= ParamSrc.m_LowZBorderCell && ZCellID <= ParamSrc.m_HighZBorderCell))
800 {
801 return true;
802 } // if
803 return false;
804
805 } // Index::ZCellInteresting
806
SubCellInteresting(int32_t SubCellID,int32_t XCellID,int32_t YCellID,IndexData const & ParamSrc)807 bool Index::SubCellInteresting(int32_t SubCellID, int32_t XCellID, int32_t YCellID, IndexData const& ParamSrc)
808 {
809 bool XGood = false, YGood = false;
810
811 // only need test sub cell if border cell
812 if (ParamSrc.m_noFilterX || (XCellID >= ParamSrc.m_LowXCellCompletelyIn && XCellID <= ParamSrc.m_HighXCellCompletelyIn))
813 {
814 XGood = true;
815 } // if
816 else
817 {
818 if (XCellID == ParamSrc.m_LowXBorderCell)
819 {
820 // left (low) border cell
821 if (SubCellID == 0 || SubCellID == 2)
822 {
823 // low half of cell in X
824 // only true if boundary of search area falls in the left (lower) half of the cell
825 if (ParamSrc.m_LowXBorderPartCell <= .5) // .5 exactly is part of low half of cell
826 XGood = true;
827 } // if
828 else
829 {
830 // high half of cell in X
831 // always true if boundary of search area falls anywhere in the cell
832 XGood = true;
833 } // else
834 } // if
835 else
836 {
837 // right (upper) border cell
838 if (SubCellID == 0 || SubCellID == 2)
839 {
840 // low half of cell in X
841 // always true if boundary of search area falls anywhere in the cell
842 XGood = true;
843 } // if
844 else
845 {
846 // high half of cell in X
847 // only true if boundary of search area falls in the right (upper) half of the cell
848 if (ParamSrc.m_HighXBorderPartCell > .5)
849 XGood = true;
850 } // else
851 } // else
852 } // else
853 if (ParamSrc.m_noFilterY || (YCellID >= ParamSrc.m_LowYCellCompletelyIn && YCellID <= ParamSrc.m_HighYCellCompletelyIn))
854 {
855 YGood = true;
856 } // if
857 else
858 {
859 if (YCellID == ParamSrc.m_LowYBorderCell)
860 {
861 // bottom (low) border cell
862 if (SubCellID == 0 || SubCellID == 1)
863 {
864 // low half of cell in Y
865 // only true if boundary of search area falls in the bottom (lower) half of the cell
866 if (ParamSrc.m_LowYBorderPartCell <= .5) // .5 exactly is part of low half of cell
867 YGood = true;
868 } // if
869 else
870 {
871 // high half of cell in Y
872 // always true if boundary of search area falls anywhere in the cell
873 YGood = true;
874 } // else
875 } // if
876 else
877 {
878 // top (upper) border cell
879 if (SubCellID == 0 || SubCellID == 1)
880 {
881 // low half of cell in Y
882 // always true if boundary of search area falls anywhere in the cell
883 YGood = true;
884 } // if
885 else
886 {
887 // high half of cell in Y
888 // only true if boundary of search area falls in the top (upper) half of the cell
889 if (ParamSrc.m_HighYBorderPartCell > .5)
890 YGood = true;
891 } // else
892 } // else
893 } // else
894
895 return (XGood && YGood);
896
897 } // Index::SubCellInteresting
898
FilterOnePoint(int32_t x,int32_t y,int32_t z,int32_t PointID,int32_t LastPointID,bool & LastPtRead,IndexData const & ParamSrc)899 bool Index::FilterOnePoint(int32_t x, int32_t y, int32_t z, int32_t PointID, int32_t LastPointID, bool &LastPtRead,
900 IndexData const& ParamSrc)
901 {
902 bool XGood = false, YGood = false, ZGood = false, PtRead = false;
903 double PtX, PtY = 0.0, PtZ = 0.0;
904
905 // filtering turned on for X?
906 if (ParamSrc.m_noFilterX)
907 XGood = true;
908 // container cell in the region of the filter that requires no individual point testing?
909 else if (x >= ParamSrc.m_LowXCellCompletelyIn && x <= ParamSrc.m_HighXCellCompletelyIn)
910 XGood = true;
911 // container cell in the region of the filter that does require individual point testing?
912 else if (x == ParamSrc.m_LowXBorderCell || x == ParamSrc.m_HighXBorderCell)
913 {
914 // save a file seek if it is the subsequent point from the last one read
915 if (PointID == LastPointID + 1)
916 {
917 if (LastPtRead)
918 {
919 PtRead = m_reader->ReadNextPoint();
920 } // if
921 } // if
922 if (! PtRead)
923 {
924 // seek and read
925 assert(static_cast<uint32_t>(PointID) < m_pointRecordsCount);
926 PtRead = (m_reader->Seek(PointID) && m_reader->ReadNextPoint());
927 } // if
928 if (PtRead)
929 {
930 Point const& TestPt = m_reader->GetPoint();
931 PtX = TestPt.GetX();
932 PtY = TestPt.GetY();
933 PtZ = TestPt.GetZ();
934 if (PtX >= ParamSrc.GetMinFilterX() && PtX <= ParamSrc.GetMaxFilterX())
935 XGood = true;
936 } // if
937 } // else
938 if (XGood)
939 {
940 // filtering turned on for Y?
941 if (ParamSrc.m_noFilterY)
942 YGood = true;
943 // container cell in the region of the filter that requires no individual point testing?
944 else if (y >= ParamSrc.m_LowYCellCompletelyIn && y <= ParamSrc.m_HighYCellCompletelyIn)
945 YGood = true;
946 // container cell in the region of the filter that does require individual point testing?
947 else if (y == ParamSrc.m_LowYBorderCell || y == ParamSrc.m_HighYBorderCell)
948 {
949 if (PtRead)
950 {
951 // we already have the data for this point
952 if (PtY >= ParamSrc.GetMinFilterY() && PtY <= ParamSrc.GetMaxFilterY())
953 YGood = true;
954 }
955 else
956 {
957 // save a file seek if it is the subsequent point from the last one read
958 if (PointID == LastPointID + 1)
959 {
960 if (LastPtRead)
961 {
962 PtRead = m_reader->ReadNextPoint();
963 } // if
964 } // if
965 if (! PtRead)
966 {
967 // seek and read
968 assert(static_cast<uint32_t>(PointID) < m_pointRecordsCount);
969 PtRead = (m_reader->Seek(PointID) && m_reader->ReadNextPoint());
970 } // if
971 if (PtRead)
972 {
973 Point const& TestPt = m_reader->GetPoint();
974 PtY = TestPt.GetY();
975 PtZ = TestPt.GetZ();
976 if (PtY >= ParamSrc.GetMinFilterY() && PtY <= ParamSrc.GetMaxFilterY())
977 YGood = true;
978 } // if
979 } // else
980 } // else if
981 } // if
982 if (XGood && YGood)
983 {
984 // filtering turned on for Z?
985 if (ParamSrc.m_noFilterZ)
986 ZGood = true;
987 // container cell in the region of the filter that requires no individual point testing?
988 else if (z >= ParamSrc.m_LowZCellCompletelyIn && z <= ParamSrc.m_HighZCellCompletelyIn)
989 ZGood = true;
990 // container cell in the region of the filter that does require individual point testing?
991 else if (z == ParamSrc.m_LowZBorderCell || z == ParamSrc.m_HighZBorderCell)
992 {
993 if (PtRead)
994 {
995 // we already have the data for this point
996 if (PtZ >= ParamSrc.GetMinFilterZ() && PtZ <= ParamSrc.GetMaxFilterZ())
997 ZGood = true;
998 }
999 else
1000 {
1001 // save a file seek if it is the subsequent point from the last one read
1002 if (PointID == LastPointID + 1)
1003 {
1004 if (LastPtRead)
1005 {
1006 PtRead = m_reader->ReadNextPoint();
1007 } // if
1008 } // if
1009 if (! PtRead)
1010 {
1011 // seek and read
1012 assert(static_cast<uint32_t>(PointID) < m_pointRecordsCount);
1013 PtRead = (m_reader->Seek(PointID) && m_reader->ReadNextPoint());
1014 } // if
1015 if (PtRead)
1016 {
1017 Point const& TestPt = m_reader->GetPoint();
1018 PtZ = TestPt.GetZ();
1019 if (PtZ >= ParamSrc.GetMinFilterZ() && PtZ <= ParamSrc.GetMaxFilterZ())
1020 ZGood = true;
1021 } // if
1022 } // else
1023 } // else if
1024 } // if
1025 LastPtRead = PtRead;
1026 return (XGood && YGood && ZGood);
1027
1028 } // Index::FilterOnePoint
1029
BuildIndex(void)1030 bool Index::BuildIndex(void)
1031 {
1032 // Build an array of two dimensions. Sort data points into
1033 uint32_t MaximumCells = LIBLAS_INDEX_MAXCELLS;
1034 m_versionMajor = LIBLAS_INDEX_VERSIONMAJOR;
1035 m_versionMinor = LIBLAS_INDEX_VERSIONMINOR;
1036
1037 // reset to beginning of point data records in case points had been examined before index is built
1038 m_reader->Seek(0);
1039 // need the header to get number of point records
1040 m_pointRecordsCount = m_pointheader.GetPointRecordsCount();
1041 // get the bounds of the data and scale factors in case they are needed for point translation
1042 m_bounds = Bounds<double>(m_pointheader.GetMinX(), m_pointheader.GetMinY(), m_pointheader.GetMinZ(), m_pointheader.GetMaxX(), m_pointheader.GetMaxY(), m_pointheader.GetMaxZ());
1043 try {
1044 m_bounds.verify();
1045 } // try
1046 catch (std::runtime_error) {
1047 return (InputBoundsError("Index::BuildIndex"));
1048 } // catch
1049 CalcRangeX();
1050 CalcRangeY();
1051 CalcRangeZ();
1052
1053 if (m_cellSizeZ > 0.0 && ! detail::compare_distance(m_cellSizeZ, 0.0))
1054 m_cellsZ = static_cast<uint32_t>(ceil(m_rangeZ / m_cellSizeZ));
1055 else
1056 m_cellsZ = 1;
1057
1058 // under the conditions of one dimension (x or y) being 0 or negative in size, no index is possible
1059 if ((m_bounds.max)(0) <= (m_bounds.min)(0) || (m_bounds.max)(1) <= (m_bounds.min)(1))
1060 {
1061 return (PointBoundsError("Index::BuildIndex"));
1062 } // if
1063
1064 // fix a cell size and number of cells in X and Y to begin the process of indexing
1065 double XRatio = m_rangeX >= m_rangeY ? 1.0: m_rangeX / m_rangeY;
1066 double YRatio = m_rangeY >= m_rangeX ? 1.0: m_rangeY / m_rangeX;
1067
1068 m_totalCells = m_pointRecordsCount / LIBLAS_INDEX_OPTPTSPERCELL;
1069 m_totalCells = static_cast<uint32_t>(sqrt((double)m_totalCells));
1070 if (m_totalCells < 10)
1071 m_totalCells = 10; // let's set a minimum number of cells to make the effort worthwhile
1072 m_cellsX = static_cast<uint32_t>(XRatio * m_totalCells);
1073 m_cellsY = static_cast<uint32_t>(YRatio * m_totalCells);
1074 if (m_cellsX < 1)
1075 m_cellsX = 1;
1076 if (m_cellsY < 1)
1077 m_cellsY = 1;
1078 m_totalCells = m_cellsX * m_cellsY;
1079 if (m_totalCells > MaximumCells)
1080 {
1081 double CellReductionRatio = (double)MaximumCells / (double)m_totalCells;
1082 CellReductionRatio = sqrt(CellReductionRatio);
1083 m_cellsX = static_cast<uint32_t>(m_cellsX * CellReductionRatio);
1084 m_cellsY = static_cast<uint32_t>(m_cellsY * CellReductionRatio);
1085 m_totalCells = m_cellsX * m_cellsY;
1086 } // if
1087 m_cellSizeX = m_rangeX / m_cellsX;
1088 m_cellSizeY = m_rangeY / m_cellsY;
1089
1090 // print some statistics to the console
1091 if (m_debugOutputLevel > 1)
1092 {
1093 fprintf(m_debugger, "Points in file %d, Cell matrix x %d, y %d, z %d\n", m_pointRecordsCount, m_cellsX, m_cellsY,
1094 m_cellsZ);
1095 fprintf(m_debugger, "Point ranges x %.2f-%.2f, y %.2f-%.2f, z %.2f-%.2f, z range %.2f\n", (m_bounds.min)(0), (m_bounds.max)(0), (m_bounds.min)(1), (m_bounds.max)(1),
1096 (m_bounds.min)(2), (m_bounds.max)(2), m_rangeZ);
1097 } // if
1098
1099 // now we know how large our index array is going to be
1100 // we'll create a vector of that many entities
1101
1102 try {
1103 // a one dimensional array to represent cell matrix
1104 IndexCellRow IndexCellsY(m_cellsY);
1105 // a two dimensional array
1106 IndexCellDataBlock IndexCellBlock(m_cellsX, IndexCellsY);
1107 liblas::detail::IndexOutput IndexOut(this);
1108
1109 // for Z bounds debugging
1110 uint32_t ZRangeSum = 0;
1111 uint32_t PointSum = 0;
1112 // read each point in the file
1113 // figure out what cell in X and Y
1114 // test to see if it is the same as the last cell
1115 uint32_t LastCellX = static_cast<uint32_t>(~0), LastCellY = static_cast<uint32_t>(~0);
1116 liblas::detail::ElevRange ZRange;
1117 uint32_t PointID = 0;
1118 uint32_t LastPointID = 0;
1119 uint32_t PtsIndexed = 0;
1120 uint32_t PointsInMemory = 0, MaxPointsInMemory;
1121 MaxPointsInMemory = m_maxMemoryUsage / sizeof(liblas::detail::IndexCell);
1122 // ReadNextPoint() throws a std::out_of_range error when it hits end of range so don't
1123 // get excited when you see it in the debug output
1124 while (m_reader->ReadNextPoint())
1125 {
1126 uint32_t CurCellX, CurCellY;
1127 // analyze the point to determine its cell ID
1128 Point const& CurPt = m_reader->GetPoint();
1129 if (IdentifyCell(CurPt, CurCellX, CurCellY))
1130 {
1131 // if same cell as last point, attempt to increment the count of consecutive points for the cell
1132 // otherwise add a new point, first checking to see if the memory allocated to this process is full
1133 if (! (CurCellX == LastCellX && CurCellY == LastCellY &&
1134 IndexCellBlock[CurCellX][CurCellY].IncrementPointRecord(LastPointID)))
1135 {
1136 // if memory allocated to this process is full, write all the point data to a temp file
1137 if (m_tempFileName.size() && PointsInMemory >= MaxPointsInMemory)
1138 {
1139 if (! PurgePointsToTempFile(IndexCellBlock))
1140 return (FileError("Index::BuildIndex"));
1141 PointsInMemory = 0;
1142 } // if
1143 IndexCellBlock[CurCellX][CurCellY].AddPointRecord(PointID);
1144 LastPointID = PointID;
1145 LastCellX = CurCellX;
1146 LastCellY = CurCellY;
1147 ++PointsInMemory;
1148 } // else
1149 // update Z cell bounds
1150 IndexCellBlock[CurCellX][CurCellY].UpdateZBounds(CurPt.GetZ());
1151 } // if
1152 ++PointID;
1153 } // while
1154 // write remaining points to temp file
1155 if (m_tempFileName.size())
1156 {
1157 if (! PurgePointsToTempFile(IndexCellBlock))
1158 return (FileError("Index::BuildIndex"));
1159 } // if using temp file
1160
1161 // print some statistics to the console
1162 if (m_debugOutputLevel > 2)
1163 {
1164 if (! OutputCellStats(IndexCellBlock))
1165 {
1166 return (DebugOutputError("Index::BuildIndex"));
1167 } // if
1168 } // if
1169
1170 // Here's where it gets fun
1171 // Read the binned data from the temp file, one cell at a time
1172 // Store the data in Variable records section of the LAS file
1173 // If a cell contains too many points, subdivide the cell and save sub-cells within the cell structure
1174 // If Z-binning is desired, define the bounds of each Z zone and subdivide sort each cell's points into Z bins
1175 // Save Z bins within the cell structure.
1176
1177 if (IndexOut.InitiateOutput())
1178 {
1179 for (uint32_t x = 0; x < m_cellsX; ++x)
1180 {
1181 for (uint32_t y = 0; y < m_cellsY; ++y)
1182 {
1183 if (m_debugOutputLevel > 3)
1184 fprintf(m_debugger, "reloading %d %d\n", x, y);
1185 if (LoadCellFromTempFile(&IndexCellBlock[x][y], x, y))
1186 {
1187 ZRange = IndexCellBlock[x][y].GetZRange();
1188 // if Z-binning is specified, create Z sub-cells first
1189 // otherwise, subdivide the cell by quadrants if the number of points in the cell
1190 // exceeds LIBLAS_INDEX_MAXPTSPERCELL
1191 if ((m_cellsZ > 1 && ZRange > m_cellSizeZ) ||
1192 (IndexCellBlock[x][y].GetNumPoints() > LIBLAS_INDEX_MAXPTSPERCELL))
1193 {
1194 // walk the points in this cell and divvy them up into Z - cells or quadtree cells
1195 // create an iterator for the map
1196 // walk the map entities
1197 liblas::detail::IndexCellData::iterator MapIt = IndexCellBlock[x][y].GetFirstRecord();
1198 for (; MapIt != IndexCellBlock[x][y].GetEnd(); ++MapIt)
1199 {
1200 // get the actual point from the las file
1201 assert(MapIt->first < m_pointRecordsCount);
1202 if (m_reader->Seek(MapIt->first) && m_reader->ReadNextPoint())
1203 {
1204 uint32_t FirstPt = 0, LastCellZ = static_cast<uint32_t>(~0);
1205 uint32_t LastSubCell = static_cast<uint32_t>(~0);
1206 for (liblas::detail::ConsecPtAccumulator PtsTested = 0; PtsTested < MapIt->second; )
1207 {
1208 Point const& CurPt = m_reader->GetPoint();
1209 // Z cell subdivision takes precedence over sub-cell quadrant subdivision
1210 if (m_cellsZ > 1 && ZRange > m_cellSizeZ)
1211 {
1212 // for the number of consecutive points, identify the Z cell
1213 uint32_t CurCellZ;
1214 if (IdentifyCellZ(CurPt, CurCellZ))
1215 {
1216 // add a record to the z cell chain or increment existing record
1217 if (! (CurCellZ == LastCellZ && IndexCellBlock[x][y].IncrementZCell(CurCellZ, FirstPt)))
1218 {
1219 FirstPt = MapIt->first + PtsTested;
1220 IndexCellBlock[x][y].AddZCell(CurCellZ, FirstPt);
1221 LastCellZ = CurCellZ;
1222 } // else
1223 } // if
1224 } // if
1225 else
1226 {
1227 uint32_t CurSubCell;
1228 // for the number of consecutive points, identify the sub cell in a 2x2 matrix
1229 // 0 is lower left, 1 is lower right, 2 is upper left, 3 is upper right
1230 if (IdentifySubCell(CurPt, x, y, CurSubCell))
1231 {
1232 // add a record to the sub cell chain or increment existing record
1233 if (! (CurSubCell == LastSubCell && IndexCellBlock[x][y].IncrementSubCell(CurSubCell, FirstPt)))
1234 {
1235 FirstPt = MapIt->first + PtsTested;
1236 IndexCellBlock[x][y].AddSubCell(CurSubCell, FirstPt);
1237 LastSubCell = CurSubCell;
1238 } // else
1239 } // if
1240 } // else
1241 ++PtsTested;
1242 if (PtsTested < MapIt->second)
1243 {
1244 if (! m_reader->ReadNextPoint())
1245 return (FileError("Index::BuildIndex"));
1246 } // if
1247 } // for
1248 } // if
1249 else
1250 return (FileError("Index::BuildIndex"));
1251 } // for
1252 IndexCellBlock[x][y].RemoveMainRecords();
1253 } // if
1254 // sum the points for later debugging
1255 PtsIndexed += IndexCellBlock[x][y].GetNumPoints();
1256 // write the cell out to permanent file VLR
1257 if (! IndexOut.OutputCell(&IndexCellBlock[x][y], x, y))
1258 return (FileError("Index::BuildIndex"));
1259
1260 // some statistical stuff for z bounds debugging
1261 ZRangeSum += ZRange;
1262 ++PointSum;
1263
1264 // purge the memory for this cell
1265 IndexCellBlock[x][y].RemoveAllRecords();
1266 } // if
1267 else
1268 {
1269 return (FileError("Index::BuildIndex"));
1270 } // else
1271 } // for y
1272 } // for x
1273 // done with this baby
1274 CloseTempFile();
1275 if (! IndexOut.FinalizeOutput())
1276 return (FileError("Index::BuildIndex"));
1277 if (m_debugOutputLevel)
1278 {
1279 if (PtsIndexed < m_pointRecordsCount)
1280 {
1281 fprintf(m_debugger, "%d of %d points in las file were indexed.\n", PtsIndexed, m_pointRecordsCount);
1282 } // if
1283 } // if
1284 if (m_debugOutputLevel > 2 && PointSum)
1285 {
1286 ZRangeSum /= PointSum;
1287 fprintf(m_debugger, "Z range average per cell %d\n", ZRangeSum);
1288 } // if
1289 if (m_writestandaloneindex)
1290 {
1291 // save a standalone index file
1292 if (! SaveIndexInStandAloneFile())
1293 return false;
1294 } // if
1295 else
1296 {
1297 // resave the entire file with new index VLR's
1298 if (! SaveIndexInLASFile())
1299 return false;
1300 } // else
1301 } // if
1302 } // try
1303 catch (std::bad_alloc) {
1304 CloseTempFile();
1305 return (MemoryError("Index::BuildIndex"));
1306 } // catch
1307
1308 return true;
1309
1310 } // Index::BuildIndex
1311
IdentifyCell(Point const & CurPt,uint32_t & CurCellX,uint32_t & CurCellY) const1312 bool Index::IdentifyCell(Point const& CurPt, uint32_t& CurCellX, uint32_t& CurCellY) const
1313 {
1314 double OffsetX, OffsetY;
1315
1316 OffsetX = (CurPt.GetX() - (m_bounds.min)(0)) / m_rangeX;
1317 if (OffsetX >= 0 && OffsetX < 1.0)
1318 CurCellX = static_cast<uint32_t>(OffsetX * m_cellsX);
1319 else if (detail::compare_distance(OffsetX, 1.0))
1320 CurCellX = m_cellsX - 1;
1321 else
1322 {
1323 return (PointBoundsError("Index::IdentifyCell"));
1324 } // else
1325
1326 OffsetY = (CurPt.GetY() - (m_bounds.min)(1)) / m_rangeY;
1327 if (OffsetY >= 0 && OffsetY < 1.0)
1328 CurCellY = static_cast<uint32_t>(OffsetY * m_cellsY);
1329 else if (detail::compare_distance(OffsetY, 1.0))
1330 CurCellY = m_cellsY - 1;
1331 else
1332 {
1333 return (PointBoundsError("Index::IdentifyCell"));
1334 } // else
1335
1336 return true;
1337
1338 } // Index::IdentifyCell
1339
IdentifyCellZ(Point const & CurPt,uint32_t & CurCellZ) const1340 bool Index::IdentifyCellZ(Point const& CurPt, uint32_t& CurCellZ) const
1341 {
1342 double OffsetZ;
1343
1344 OffsetZ = (CurPt.GetZ() - (m_bounds.min)(2)) / m_rangeZ;
1345 if (OffsetZ >= 0 && OffsetZ < 1.0)
1346 CurCellZ = static_cast<uint32_t>(OffsetZ * m_cellsZ);
1347 else if (detail::compare_distance(OffsetZ, 1.0))
1348 CurCellZ = m_cellsZ - 1;
1349 else
1350 {
1351 return (PointBoundsError("Index::IdentifyCellZ"));
1352 } // else
1353
1354 return true;
1355
1356 } // Index::IdentifyCellZ
1357
IdentifySubCell(Point const & CurPt,uint32_t x,uint32_t y,uint32_t & CurSubCell) const1358 bool Index::IdentifySubCell(Point const& CurPt, uint32_t x, uint32_t y, uint32_t& CurSubCell) const
1359 {
1360 double Offset, CellMinX, CellMinY;
1361
1362 CellMinX = x * m_cellSizeX + (m_bounds.min)(0);
1363 CellMinY = y * m_cellSizeY + (m_bounds.min)(1);
1364 // find point position in X
1365 Offset = (CurPt.GetX() - CellMinX) / m_cellSizeX;
1366 if (Offset > .5) //upper half X
1367 {
1368 // find point position in Y
1369 Offset = (CurPt.GetY() - CellMinY) / m_cellSizeY;
1370 if (Offset > .5)
1371 CurSubCell = 3; // upper half of Y, NE
1372 else // <= .5
1373 CurSubCell = 1; // lower half of Y, SE
1374 } // if
1375 else // lower half X
1376 {
1377 // <= .5
1378 // find point position in Y
1379 Offset = (CurPt.GetY() - CellMinY) / m_cellSizeY;
1380 if (Offset > .5)
1381 CurSubCell = 2; // upper half of Y, NW
1382 else // <= .5
1383 CurSubCell = 0; // lower half of Y, SW
1384 } // else
1385
1386 return true;
1387
1388 } // Index::IdentifySubCell
1389
PurgePointsToTempFile(IndexCellDataBlock & CellBlock)1390 bool Index::PurgePointsToTempFile(IndexCellDataBlock& CellBlock)
1391 {
1392 if (m_tempFile || OpenTempFile())
1393 {
1394 liblas::detail::TempFileOffsetType EmptyOffset = 0; // this might not be large enough
1395
1396 if (! m_tempFileStarted)
1397 {
1398 // there is some setup of the temp file to be done first
1399 // write out a block of file offsets the size of the number of cells
1400 for (uint32_t i = 0; i < m_totalCells; ++i)
1401 {
1402 if (fwrite(&EmptyOffset, sizeof(liblas::detail::TempFileOffsetType), 1, m_tempFile) < 1)
1403 {
1404 return (FileError("Index::PurgePointsToTempFile"));
1405 } // if error
1406 } // for
1407 m_tempFileWrittenBytes = m_totalCells * sizeof(liblas::detail::TempFileOffsetType);
1408 m_tempFileStarted = true;
1409 } // if
1410 for (uint32_t x = 0; x < m_cellsX; ++x)
1411 {
1412 for (uint32_t y = 0; y < m_cellsY; ++y)
1413 {
1414 uint32_t RecordsToWrite = CellBlock[x][y].GetNumRecords();
1415 if (RecordsToWrite)
1416 {
1417 // write the current file location in the cell block header
1418 // if cell block header is 0 write the current file location in the file header
1419 // otherwise write the current file location at the file location specified in the
1420 // cell block header
1421 liblas::detail::TempFileOffsetType LastWriteLocation = CellBlock[x][y].GetFileOffset();
1422 if (LastWriteLocation == 0)
1423 LastWriteLocation = (x * m_cellsY + y) * sizeof(liblas::detail::TempFileOffsetType);
1424 #ifdef _MSC_VER
1425 _fseeki64(m_tempFile, LastWriteLocation, SEEK_SET);
1426 #else
1427 fseek(m_tempFile, LastWriteLocation, SEEK_SET);
1428 #endif
1429 if (fwrite(&m_tempFileWrittenBytes, sizeof(liblas::detail::TempFileOffsetType), 1, m_tempFile) < 1)
1430 return (FileError("Index::PurgePointsToTempFile"));
1431 CellBlock[x][y].SetFileOffset(m_tempFileWrittenBytes);
1432
1433 // seek to end of file where next block of data will be written
1434 #ifdef _MSC_VER
1435 _fseeki64(m_tempFile, 0, SEEK_END);
1436 #else
1437 fseek(m_tempFile, 0, SEEK_END);
1438 #endif
1439
1440 // write a blank space for later placement of next file block for this cell
1441 if (fwrite(&EmptyOffset, sizeof(liblas::detail::TempFileOffsetType), 1, m_tempFile) < 1)
1442 return (FileError("Index::PurgePointsToTempFile"));
1443 m_tempFileWrittenBytes += sizeof(liblas::detail::TempFileOffsetType);
1444 // write the number of records stored in this section
1445 if (fwrite(&RecordsToWrite, sizeof(uint32_t), 1, m_tempFile) < 1)
1446 return (FileError("Index::PurgePointsToTempFile"));
1447 m_tempFileWrittenBytes += sizeof(uint32_t);
1448
1449 liblas::detail::IndexCellData::iterator MapIt = CellBlock[x][y].GetFirstRecord();
1450 for (uint32_t RecordNum = 0; RecordNum < RecordsToWrite && MapIt != CellBlock[x][y].GetEnd(); ++RecordNum, ++MapIt)
1451 {
1452 // write the point ID
1453 uint32_t PointID = MapIt->first;
1454 // write the number of consecutive points
1455 liblas::detail::ConsecPtAccumulator ConsecutivePoints = MapIt->second;
1456 if (fwrite(&PointID, sizeof(uint32_t), 1, m_tempFile) < 1)
1457 return (FileError("Index::PurgePointsToTempFile"));
1458 if (fwrite(&ConsecutivePoints, sizeof(liblas::detail::ConsecPtAccumulator), 1, m_tempFile) < 1)
1459 return (FileError("Index::PurgePointsToTempFile"));
1460 m_tempFileWrittenBytes += sizeof(uint32_t);
1461 m_tempFileWrittenBytes += sizeof(liblas::detail::ConsecPtAccumulator);
1462 } // for
1463 // purge the records for this cell from active memory
1464 CellBlock[x][y].RemoveMainRecords();
1465 } // if
1466 } // for y
1467 } // for x
1468 // necessary for subsequent reads in case fseek isn't called first
1469 fflush(m_tempFile);
1470 return true;
1471 } // if file
1472
1473 return (FileError("Index::PurgePointsToTempFile"));
1474
1475 } // Index::PurgePointsToTempFile
1476
LoadCellFromTempFile(liblas::detail::IndexCell * CellBlock,uint32_t CurCellX,uint32_t CurCellY)1477 bool Index::LoadCellFromTempFile(liblas::detail::IndexCell *CellBlock,
1478 uint32_t CurCellX, uint32_t CurCellY)
1479 {
1480
1481 uint32_t RecordsToRead, FormerNumPts, NewNumPts = 0;
1482 liblas::detail::TempFileOffsetType FileOffset;
1483
1484 FormerNumPts = CellBlock->GetNumPoints();
1485 CellBlock->SetNumPoints(0);
1486
1487 // load the cell as it was written
1488 // read the first offset for this cell
1489
1490 #ifdef _MSC_VER
1491 if (_fseeki64(m_tempFile, (CurCellX * m_cellsY + CurCellY) * sizeof (liblas::detail::TempFileOffsetType), SEEK_SET))
1492 #else
1493 if (fseek(m_tempFile, (CurCellX * m_cellsY + CurCellY) * sizeof (liblas::detail::TempFileOffsetType), SEEK_SET))
1494 #endif
1495 return (FileError("Index::LoadCellFromTempFile"));
1496 if (fread(&FileOffset, sizeof (liblas::detail::TempFileOffsetType), 1, m_tempFile) < 1)
1497 return (FileError("Index::LoadCellFromTempFile"));
1498 while (FileOffset > 0)
1499 {
1500 // jump to the first block for this cell, read the next offset
1501
1502 #ifdef _MSC_VER
1503 if (_fseeki64(m_tempFile, FileOffset, SEEK_SET))
1504 #else
1505 if (fseek(m_tempFile, FileOffset, SEEK_SET))
1506 #endif
1507
1508 return (FileError("Index::LoadCellFromTempFile"));
1509 if (fread(&FileOffset, sizeof (liblas::detail::TempFileOffsetType), 1, m_tempFile) < 1)
1510 return (FileError("Index::LoadCellFromTempFile"));
1511 // read the data for the cell in this block
1512 // first is the number of items to read now
1513 if (fread(&RecordsToRead, sizeof (uint32_t), 1, m_tempFile) < 1)
1514 return (FileError("Index::LoadCellFromTempFile"));
1515 for (uint32_t RecordNum = 0; RecordNum < RecordsToRead; ++RecordNum)
1516 {
1517 uint32_t PointID;
1518 liblas::detail::ConsecPtAccumulator ConsecutivePoints;
1519 // read the point ID
1520 if (fread(&PointID, sizeof(uint32_t), 1, m_tempFile) < 1)
1521 return (FileError("Index::LoadCellFromTempFile"));
1522 // read the number of consecutive points
1523 if (fread(&ConsecutivePoints, sizeof(liblas::detail::ConsecPtAccumulator), 1, m_tempFile) < 1)
1524 return (FileError("Index::LoadCellFromTempFile"));
1525 CellBlock->AddPointRecord(PointID, ConsecutivePoints);
1526 } // for
1527 } // while
1528 // check to see that we got the number of points back that we started with
1529 NewNumPts = CellBlock->GetNumPoints();
1530 if (NewNumPts != FormerNumPts)
1531 {
1532 CloseTempFile();
1533 return (PointCountError("Index::LoadCellFromTempFile"));
1534 } // if
1535 return (true);
1536
1537 } // Index::LoadCellFromTempFile
1538
OpenTempFile(void)1539 FILE *Index::OpenTempFile(void)
1540 {
1541
1542 m_tempFileStarted = 0;
1543 m_tempFileWrittenBytes = 0;
1544 return (m_tempFile = fopen(m_tempFileName.c_str(), "wb+"));
1545
1546 } // Index::OpenTempFile
1547
CloseTempFile(void)1548 void Index::CloseTempFile(void)
1549 {
1550
1551 if (m_tempFile)
1552 {
1553 fclose(m_tempFile);
1554 remove(m_tempFileName.c_str());
1555 } // if
1556 m_tempFile = 0;
1557 m_tempFileWrittenBytes = 0;
1558
1559 } // Index::CloseTempFile
1560
SaveIndexInLASFile(void)1561 bool Index::SaveIndexInLASFile(void)
1562 {
1563 try {
1564 Writer writer(*m_ofs, m_idxheader);
1565 m_reader->Seek(0);
1566 while (m_reader->ReadNextPoint())
1567 {
1568 Point const& CurPt = m_reader->GetPoint();
1569 if (! writer.WritePoint(CurPt))
1570 return (OutputFileError("Index::SaveIndexInLASFile"));
1571 } // while
1572 } // try
1573 catch (std::runtime_error) {
1574 return (OutputFileError("Index::SaveIndexInLASFile"));
1575 } // catch
1576 return true;
1577 } // Index::SaveIndexInLASFile
1578
SaveIndexInStandAloneFile(void)1579 bool Index::SaveIndexInStandAloneFile(void)
1580 {
1581 try {
1582 Writer writer(*m_ofs, m_idxheader);
1583 /* test block - uncommenting this makes it just like above version with included points
1584 m_reader->Seek(0);
1585 while (m_reader->ReadNextPoint())
1586 {
1587 Point const& CurPt = m_reader->GetPoint();
1588 if (! writer.WritePoint(CurPt))
1589 return (OutputFileError("Index::SaveIndexInLASFile"));
1590 } // while
1591 */
1592 } // try
1593 catch (std::runtime_error) {
1594 return (OutputFileError("Index::SaveIndexInStandAloneFile"));
1595 } // catch
1596 return true;
1597 } // Index::SaveIndexInStandAloneFile
1598
FileError(const char * Reporter)1599 bool Index::FileError(const char *Reporter)
1600 {
1601
1602 CloseTempFile();
1603 if (m_debugOutputLevel)
1604 fprintf(m_debugger, "File i/o error, %s\n", Reporter);
1605 return false;
1606
1607 } // Index::FileError
1608
InputFileError(const char * Reporter) const1609 bool Index::InputFileError(const char *Reporter) const
1610 {
1611
1612 if (m_debugOutputLevel)
1613 fprintf(m_debugger, "Input file i/o error, %s\n", Reporter);
1614 return false;
1615
1616 } // Index::InputFileError
1617
OutputFileError(const char * Reporter) const1618 bool Index::OutputFileError(const char *Reporter) const
1619 {
1620
1621 if (m_debugOutputLevel)
1622 fprintf(m_debugger, "Output file i/o error, %s\n", Reporter);
1623 return false;
1624
1625 } // Index::OutputFileError
1626
DebugOutputError(const char * Reporter) const1627 bool Index::DebugOutputError(const char *Reporter) const
1628 {
1629
1630 if (m_debugOutputLevel)
1631 fprintf(m_debugger, "Debug output error, %s\n", Reporter);
1632 return false;
1633
1634 } // Index::DebugOutputError
1635
PointCountError(const char * Reporter) const1636 bool Index::PointCountError(const char *Reporter) const
1637 {
1638
1639 if (m_debugOutputLevel)
1640 fprintf(m_debugger, "Point checksum error, %s\n", Reporter);
1641 return false;
1642
1643 } // Index::PointCountError
1644
PointBoundsError(const char * Reporter) const1645 bool Index::PointBoundsError(const char *Reporter) const
1646 {
1647
1648 if (m_debugOutputLevel)
1649 fprintf(m_debugger, "Point out of bounds error, %s\n", Reporter);
1650 return false;
1651
1652 } // Index::PointBoundsError
1653
MemoryError(const char * Reporter) const1654 bool Index::MemoryError(const char *Reporter) const
1655 {
1656
1657 if (m_debugOutputLevel)
1658 fprintf(m_debugger, "Memory error, %s\n", Reporter);
1659 return false;
1660
1661 } // Index::MemoryError
1662
InitError(const char * Reporter) const1663 bool Index::InitError(const char *Reporter) const
1664 {
1665
1666 if (m_debugOutputLevel)
1667 fprintf(m_debugger, "Index creation failure, %s\n", Reporter);
1668 return false;
1669
1670 } // Index::InitError
1671
InputBoundsError(const char * Reporter) const1672 bool Index::InputBoundsError(const char *Reporter) const
1673 {
1674
1675 if (m_debugOutputLevel)
1676 fprintf(m_debugger, "Input file has inappropriate bounds, %s\n", Reporter);
1677 return false;
1678
1679 } // Index::InputBoundsError
1680
1681 #define LIBLAS_INDEX_DEBUGCELLBINS 20
1682
OutputCellStats(IndexCellDataBlock & CellBlock) const1683 bool Index::OutputCellStats(IndexCellDataBlock& CellBlock) const
1684 {
1685 uint32_t MaxPointsPerCell = 0;
1686
1687 for (uint32_t x = 0; x < m_cellsX; ++x)
1688 {
1689 for (uint32_t y = 0; y < m_cellsY; ++y)
1690 {
1691 uint32_t PointsThisCell = CellBlock[x][y].GetNumPoints();
1692 if (PointsThisCell > MaxPointsPerCell)
1693 MaxPointsPerCell = PointsThisCell;
1694 } // for
1695 } // for
1696
1697 std::vector<uint32_t> CellPopulation(LIBLAS_INDEX_DEBUGCELLBINS);
1698
1699 for (uint32_t x = 0; x < m_cellsX; ++x)
1700 {
1701 for (uint32_t y = 0; y < m_cellsY; ++y)
1702 {
1703 uint32_t PointsThisCell = CellBlock[x][y].GetNumPoints();
1704 uint32_t BinThisCell = (uint32_t )(LIBLAS_INDEX_DEBUGCELLBINS * (double)PointsThisCell / (double)MaxPointsPerCell);
1705 if (BinThisCell >= LIBLAS_INDEX_DEBUGCELLBINS)
1706 BinThisCell = LIBLAS_INDEX_DEBUGCELLBINS - 1;
1707 ++CellPopulation[BinThisCell];
1708 } // for
1709 } // for
1710
1711 fprintf(m_debugger, "Max points per cell %d\n", MaxPointsPerCell);
1712 OutputCellGraph(CellPopulation, MaxPointsPerCell);
1713 // no way for this to fail at this time but allow for future modifications by having a return value
1714 return true;
1715
1716 } // Index::OutputCellStats
1717
OutputCellGraph(std::vector<uint32_t> CellPopulation,uint32_t MaxPointsPerCell) const1718 bool Index::OutputCellGraph(std::vector<uint32_t> CellPopulation, uint32_t MaxPointsPerCell) const
1719 {
1720
1721 for (uint32_t i = 0; i < CellPopulation.size(); ++i)
1722 {
1723 fprintf(m_debugger,"Bin %2d (%4d-%4d)... Cells in point range bin %d\n", i, (i * MaxPointsPerCell / LIBLAS_INDEX_DEBUGCELLBINS),
1724 ((i + 1)* MaxPointsPerCell / LIBLAS_INDEX_DEBUGCELLBINS), CellPopulation[i]);
1725 } // for
1726 // no way for this to fail at this time but allow for future modifications by having a return value
1727 return true;
1728
1729 } // Index::OutputCellGraph
1730
GetIndexAuthorStr(void) const1731 const char *Index::GetIndexAuthorStr(void) const
1732 {
1733 return (m_indexAuthor.c_str());
1734 } // Index::GetIndexAuthorStr
1735
GetIndexCommentStr(void) const1736 const char *Index::GetIndexCommentStr(void) const
1737 {
1738 return (m_indexComment.c_str());
1739 } // Index::GetIndexAuthorStr
1740
GetIndexDateStr(void) const1741 const char *Index::GetIndexDateStr(void) const
1742 {
1743 return (m_indexDate.c_str());
1744 } // Index::GetIndexAuthorStr
1745
IndexData(void)1746 IndexData::IndexData(void)
1747 {
1748 SetValues();
1749 } // IndexData::IndexData
1750
IndexData(Index const & index)1751 IndexData::IndexData(Index const& index)
1752 {
1753 SetValues();
1754 m_reader = index.GetReader();
1755 m_idxreader = index.GetIndexReader();
1756 m_filter = index.GetBounds();
1757 m_debugOutputLevel = index.GetDebugOutputLevel();
1758 m_tempFileName = index.GetTempFileName() ? index.GetTempFileName(): "";
1759 m_indexAuthor = index.GetIndexAuthorStr() ? index.GetIndexAuthorStr(): "";
1760 m_indexComment = index.GetIndexCommentStr() ? index.GetIndexCommentStr(): "";
1761 m_indexDate = index.GetIndexDateStr() ? index.GetIndexDateStr(): "";
1762 m_cellSizeZ = index.GetCellSizeZ();
1763 m_debugger = index.GetDebugger() ? index.GetDebugger(): stderr;
1764 m_readOnly = index.GetReadOnly();
1765 m_forceNewIndex = index.GetForceNewIndex();
1766 if (index.GetMaxMemoryUsage() > 0)
1767 m_maxMemoryUsage = index.GetMaxMemoryUsage();
1768 else
1769 m_maxMemoryUsage = LIBLAS_INDEX_MAXMEMDEFAULT;
1770 if (m_maxMemoryUsage < LIBLAS_INDEX_MINMEMDEFAULT)
1771 m_maxMemoryUsage = LIBLAS_INDEX_MINMEMDEFAULT;
1772 m_indexValid = index.IndexReady();
1773 } // IndexData::IndexData
1774
IndexData(IndexData const & other)1775 IndexData::IndexData(IndexData const& other)
1776 {
1777 Copy(other);
1778 } // IndexData::IndexData
1779
operator =(IndexData const & rhs)1780 IndexData& IndexData::operator=(IndexData const& rhs)
1781 {
1782 Copy(rhs);
1783 return (*this);
1784 } // IndexData::operator=
1785
Copy(IndexData const & other)1786 void IndexData::Copy(IndexData const& other)
1787 {
1788 if (&other != this)
1789 {
1790 m_reader = other.m_reader;
1791 m_idxreader = other.m_idxreader;
1792 m_iterator = other.m_iterator;
1793 m_ifs = other.m_ifs;
1794 m_ofs = other.m_ofs;
1795 m_tempFileName = other.m_tempFileName;
1796 m_indexAuthor = other.m_indexAuthor;
1797 m_indexComment = other.m_indexComment;
1798 m_indexDate = other.m_indexDate;
1799 m_cellSizeZ = other.m_cellSizeZ;
1800 m_maxMemoryUsage = other.m_maxMemoryUsage;
1801 m_debugOutputLevel = other.m_debugOutputLevel;
1802 m_readOnly = other.m_readOnly;
1803 m_writestandaloneindex = other.m_writestandaloneindex;
1804 m_forceNewIndex = other.m_forceNewIndex;
1805 m_debugger = other.m_debugger;
1806 m_indexValid = other.m_indexValid;
1807 m_noFilterX = other.m_noFilterX;
1808 m_noFilterY = other.m_noFilterY;
1809 m_noFilterZ = other.m_noFilterZ;
1810 m_filter = other.m_filter;
1811 m_LowXCellCompletelyIn = other.m_LowXCellCompletelyIn;
1812 m_HighXCellCompletelyIn = other.m_HighXCellCompletelyIn;
1813 m_LowYCellCompletelyIn = other.m_LowYCellCompletelyIn;
1814 m_HighYCellCompletelyIn = other.m_HighYCellCompletelyIn;
1815 m_LowZCellCompletelyIn = other.m_LowZCellCompletelyIn;
1816 m_HighZCellCompletelyIn = other.m_HighZCellCompletelyIn;
1817 m_LowXBorderCell = other.m_LowXBorderCell;
1818 m_HighXBorderCell = other.m_HighXBorderCell;
1819 m_LowYBorderCell = other.m_LowYBorderCell;
1820 m_HighYBorderCell = other.m_HighYBorderCell;
1821 m_LowZBorderCell = other.m_LowZBorderCell;
1822 m_HighZBorderCell = other.m_HighZBorderCell;
1823 m_LowXBorderPartCell = other.m_LowXBorderPartCell;
1824 m_HighXBorderPartCell = other.m_HighXBorderPartCell;
1825 m_LowYBorderPartCell = other.m_LowYBorderPartCell;
1826 m_HighYBorderPartCell = other.m_HighYBorderPartCell;
1827 } // if
1828 } // IndexData::Copy
1829
SetValues(void)1830 void IndexData::SetValues(void)
1831 {
1832 m_reader = 0;
1833 m_idxreader = 0;
1834 m_iterator = 0;
1835 m_ifs = 0;
1836 m_ofs = 0;
1837 m_tempFileName = 0;
1838 m_indexAuthor = 0;
1839 m_indexComment = 0;
1840 m_indexDate = 0;
1841 m_cellSizeZ = 0.0;
1842 m_maxMemoryUsage = 0;
1843 m_debugOutputLevel = 0;
1844 m_readOnly = false;
1845 m_writestandaloneindex = false;
1846 m_forceNewIndex = false;
1847 m_debugger = 0;
1848 m_indexValid = false;
1849 m_noFilterX = m_noFilterY = m_noFilterZ = false;
1850 m_LowXCellCompletelyIn = m_HighXCellCompletelyIn = m_LowYCellCompletelyIn = m_HighYCellCompletelyIn =
1851 m_LowZCellCompletelyIn = m_HighZCellCompletelyIn =
1852 m_LowXBorderCell = m_HighXBorderCell = m_LowYBorderCell = m_HighYBorderCell =
1853 m_LowZBorderCell = m_HighZBorderCell = 0;
1854 m_LowXBorderPartCell = m_HighXBorderPartCell = m_LowYBorderPartCell = m_HighYBorderPartCell = 0.0;
1855 } // IndexData::SetValues
1856
SetInitialValues(std::istream * ifs,Reader * reader,std::ostream * ofs,Reader * idxreader,const char * tmpfilenme,const char * indexauthor,const char * indexcomment,const char * indexdate,double zbinht,uint32_t maxmem,int debugoutputlevel,bool readonly,bool writestandaloneindex,bool forcenewindex,FILE * debugger)1857 bool IndexData::SetInitialValues(std::istream *ifs, Reader *reader, std::ostream *ofs, Reader *idxreader,
1858 const char *tmpfilenme, const char *indexauthor,
1859 const char *indexcomment, const char *indexdate, double zbinht,
1860 uint32_t maxmem, int debugoutputlevel, bool readonly, bool writestandaloneindex,
1861 bool forcenewindex, FILE *debugger)
1862 {
1863
1864 m_ifs = ifs;
1865 m_ofs = ofs;
1866 m_reader = reader;
1867 m_idxreader = idxreader;
1868 m_iterator = 0;
1869 m_tempFileName = tmpfilenme;
1870 m_indexAuthor = indexauthor;
1871 m_indexComment = indexcomment;
1872 m_indexDate = indexdate;
1873 m_cellSizeZ = zbinht;
1874 m_maxMemoryUsage = maxmem;
1875 m_debugOutputLevel = debugoutputlevel;
1876 m_readOnly = readonly;
1877 m_writestandaloneindex = writestandaloneindex;
1878 m_forceNewIndex = forcenewindex;
1879 m_debugger = debugger;
1880 m_indexValid = false;
1881 m_LowXCellCompletelyIn = m_HighXCellCompletelyIn = m_LowYCellCompletelyIn = m_HighYCellCompletelyIn =
1882 m_LowZCellCompletelyIn = m_HighZCellCompletelyIn =
1883 m_LowXBorderCell = m_HighXBorderCell = m_LowYBorderCell = m_HighYBorderCell =
1884 m_LowZBorderCell = m_HighZBorderCell = 0;
1885 m_LowXBorderPartCell = m_HighXBorderPartCell = m_LowYBorderPartCell = m_HighYBorderPartCell = 0.0;
1886 return (m_reader || m_ifs);
1887
1888 } // IndexData::SetInitialValues
1889
SetBuildEmbedValues(Reader * reader,std::ostream * ofs,const char * tmpfilenme,const char * indexauthor,const char * indexcomment,const char * indexdate,double zbinht,uint32_t maxmem,int debugoutputlevel,FILE * debugger)1890 bool IndexData::SetBuildEmbedValues(Reader *reader, std::ostream *ofs, const char *tmpfilenme, const char *indexauthor,
1891 const char *indexcomment, const char *indexdate, double zbinht,
1892 uint32_t maxmem, int debugoutputlevel, FILE *debugger)
1893 {
1894
1895 m_ifs = 0;
1896 m_ofs = ofs;
1897 m_reader = reader;
1898 m_idxreader = 0;
1899 m_iterator = 0;
1900 m_tempFileName = tmpfilenme;
1901 m_indexAuthor = indexauthor;
1902 m_indexComment = indexcomment;
1903 m_indexDate = indexdate;
1904 m_cellSizeZ = zbinht;
1905 m_maxMemoryUsage = maxmem;
1906 m_debugOutputLevel = debugoutputlevel;
1907 m_readOnly = false;
1908 m_writestandaloneindex = false;
1909 m_forceNewIndex = true;
1910 m_debugger = debugger;
1911 m_indexValid = false;
1912 return (m_reader && m_ofs && m_tempFileName);
1913
1914 } // IndexData::SetBuildEmbedValues
1915
SetBuildAloneValues(Reader * reader,std::ostream * ofs,const char * tmpfilenme,const char * indexauthor,const char * indexcomment,const char * indexdate,double zbinht,uint32_t maxmem,int debugoutputlevel,FILE * debugger)1916 bool IndexData::SetBuildAloneValues(Reader *reader, std::ostream *ofs, const char *tmpfilenme, const char *indexauthor,
1917 const char *indexcomment, const char *indexdate, double zbinht,
1918 uint32_t maxmem, int debugoutputlevel, FILE *debugger)
1919 {
1920
1921 m_ifs = 0;
1922 m_ofs = ofs;
1923 m_reader = reader;
1924 m_idxreader = 0;
1925 m_iterator = 0;
1926 m_tempFileName = tmpfilenme;
1927 m_indexAuthor = indexauthor;
1928 m_indexComment = indexcomment;
1929 m_indexDate = indexdate;
1930 m_cellSizeZ = zbinht;
1931 m_maxMemoryUsage = maxmem;
1932 m_debugOutputLevel = debugoutputlevel;
1933 m_readOnly = false;
1934 m_writestandaloneindex = true;
1935 m_forceNewIndex = true;
1936 m_debugger = debugger;
1937 m_indexValid = false;
1938 return (m_reader && m_ofs && m_tempFileName);
1939
1940 } // IndexData::SetBuildAloneValues
1941
SetReadEmbedValues(Reader * reader,int debugoutputlevel,FILE * debugger)1942 bool IndexData::SetReadEmbedValues(Reader *reader, int debugoutputlevel, FILE *debugger)
1943 {
1944
1945 m_ifs = 0;
1946 m_ofs = 0;
1947 m_reader = reader;
1948 m_idxreader = 0;
1949 m_iterator = 0;
1950 m_tempFileName = 0;
1951 m_indexAuthor = 0;
1952 m_indexComment = 0;
1953 m_indexDate = 0;
1954 m_cellSizeZ = 0.0;
1955 m_maxMemoryUsage = 0;
1956 m_debugOutputLevel = debugoutputlevel;
1957 m_readOnly = true;
1958 m_writestandaloneindex = false;
1959 m_forceNewIndex = false;
1960 m_debugger = debugger;
1961 m_indexValid = false;
1962 return (m_reader ? true: false);
1963
1964 } // IndexData::SetReadEmbedValues
1965
SetReadAloneValues(Reader * reader,Reader * idxreader,int debugoutputlevel,FILE * debugger)1966 bool IndexData::SetReadAloneValues(Reader *reader, Reader *idxreader, int debugoutputlevel, FILE *debugger)
1967 {
1968
1969 m_ifs = 0;
1970 m_ofs = 0;
1971 m_reader = reader;
1972 m_idxreader = idxreader;
1973 m_iterator = 0;
1974 m_tempFileName = 0;
1975 m_indexAuthor = 0;
1976 m_indexComment = 0;
1977 m_indexDate = 0;
1978 m_cellSizeZ = 0.0;
1979 m_maxMemoryUsage = 0;
1980 m_debugOutputLevel = debugoutputlevel;
1981 m_readOnly = true;
1982 m_writestandaloneindex = false;
1983 m_forceNewIndex = false;
1984 m_debugger = debugger;
1985 m_indexValid = false;
1986 return (m_reader && m_idxreader);
1987
1988 } // IndexData::SetReadAloneValues
1989
SetReadOrBuildEmbedValues(Reader * reader,std::ostream * ofs,const char * tmpfilenme,const char * indexauthor,const char * indexcomment,const char * indexdate,double zbinht,uint32_t maxmem,int debugoutputlevel,FILE * debugger)1990 bool IndexData::SetReadOrBuildEmbedValues(Reader *reader, std::ostream *ofs, const char *tmpfilenme, const char *indexauthor,
1991 const char *indexcomment, const char *indexdate, double zbinht,
1992 uint32_t maxmem, int debugoutputlevel, FILE *debugger)
1993 {
1994
1995 SetBuildEmbedValues(reader, ofs, tmpfilenme, indexauthor, indexcomment, indexdate, zbinht,
1996 maxmem, debugoutputlevel, debugger);
1997
1998 m_forceNewIndex = false;
1999 return (m_reader && m_ofs && m_tempFileName);
2000
2001 } // IndexData::SetBuildEmbedValues
2002
SetReadOrBuildAloneValues(Reader * reader,std::ostream * ofs,const char * tmpfilenme,const char * indexauthor,const char * indexcomment,const char * indexdate,double zbinht,uint32_t maxmem,int debugoutputlevel,FILE * debugger)2003 bool IndexData::SetReadOrBuildAloneValues(Reader *reader, std::ostream *ofs, const char *tmpfilenme, const char *indexauthor,
2004 const char *indexcomment, const char *indexdate, double zbinht,
2005 uint32_t maxmem, int debugoutputlevel, FILE *debugger)
2006 {
2007
2008 SetBuildAloneValues(reader, ofs, tmpfilenme, indexauthor, indexcomment, indexdate, zbinht,
2009 maxmem, debugoutputlevel, debugger);
2010
2011 m_forceNewIndex = false;
2012 return (m_reader && m_ofs && m_tempFileName);
2013
2014 } // IndexData::SetBuildAloneValues
2015
SetFilterValues(double LowFilterX,double HighFilterX,double LowFilterY,double HighFilterY,double LowFilterZ,double HighFilterZ,Index const & index)2016 bool IndexData::SetFilterValues(double LowFilterX, double HighFilterX, double LowFilterY, double HighFilterY,
2017 double LowFilterZ, double HighFilterZ, Index const& index)
2018 {
2019 try {
2020 m_filter = Bounds<double>(LowFilterX, LowFilterY, LowFilterZ, HighFilterX, HighFilterY, HighFilterZ);
2021 m_filter.verify();
2022 m_filter.clip(index.GetBounds());
2023 } // try
2024 catch (std::runtime_error) {
2025 return (false);
2026 } // catch verification error
2027 return (CalcFilterEnablers());
2028
2029 } // IndexData::SetFilterValues
2030
SetFilterValues(Bounds<double> const & src,Index const & index)2031 bool IndexData::SetFilterValues(Bounds<double> const& src, Index const& index)
2032 {
2033 try {
2034 m_filter = src;
2035 m_filter.verify();
2036 m_filter.clip(index.GetBounds());
2037 } // try
2038 catch (std::runtime_error) {
2039 return (false);
2040 } // catch verification error
2041 return (CalcFilterEnablers());
2042
2043 } // IndexData::SetFilterValues
2044
CalcFilterEnablers(void)2045 bool IndexData::CalcFilterEnablers(void)
2046 {
2047 if (detail::compare_distance((m_filter.min)(0), (m_filter.max)(0)))
2048 m_noFilterX = true;
2049 if (detail::compare_distance((m_filter.min)(1), (m_filter.max)(1)))
2050 m_noFilterY = true;
2051 if (detail::compare_distance((m_filter.min)(2), (m_filter.max)(2)))
2052 m_noFilterZ = true;
2053 return (! (m_noFilterX && m_noFilterY && m_noFilterZ));
2054 } // IndexData::CalcFilterEnablers
2055
ClampFilterBounds(Bounds<double> const & m_bounds)2056 void IndexData::ClampFilterBounds(Bounds<double> const& m_bounds)
2057 {
2058 m_filter.clip(m_bounds);
2059 } // IndexData::ClampFilterBounds
2060
IndexIterator(Index * IndexSrc,double LowFilterX,double HighFilterX,double LowFilterY,double HighFilterY,double LowFilterZ,double HighFilterZ,uint32_t ChunkSize)2061 IndexIterator::IndexIterator(Index *IndexSrc, double LowFilterX, double HighFilterX, double LowFilterY, double HighFilterY,
2062 double LowFilterZ, double HighFilterZ, uint32_t ChunkSize)
2063 : m_indexData(*IndexSrc)
2064 {
2065 m_index = IndexSrc;
2066 m_indexData.m_filter = Bounds<double>(LowFilterX, LowFilterY, LowFilterZ, HighFilterX, HighFilterY, HighFilterZ);
2067 m_chunkSize = ChunkSize;
2068 m_advance = 0;
2069 ResetPosition();
2070 } // IndexIterator::IndexIterator
2071
IndexIterator(Index * IndexSrc,IndexData const & IndexDataSrc,uint32_t ChunkSize)2072 IndexIterator::IndexIterator(Index *IndexSrc, IndexData const& IndexDataSrc, uint32_t ChunkSize)
2073 : m_indexData(IndexDataSrc)
2074 {
2075 m_index = IndexSrc;
2076 m_chunkSize = ChunkSize;
2077 m_advance = 0;
2078 ResetPosition();
2079 } // IndexIterator::IndexIterator
2080
IndexIterator(Index * IndexSrc,Bounds<double> const & BoundsSrc,uint32_t ChunkSize)2081 IndexIterator::IndexIterator(Index *IndexSrc, Bounds<double> const& BoundsSrc, uint32_t ChunkSize)
2082 {
2083 m_index = IndexSrc;
2084 m_indexData = IndexData(*IndexSrc);
2085 m_indexData.m_filter = BoundsSrc;
2086 m_chunkSize = ChunkSize;
2087 m_advance = 0;
2088 ResetPosition();
2089 } // IndexIterator::IndexIterator
2090
IndexIterator(IndexIterator const & other)2091 IndexIterator::IndexIterator(IndexIterator const& other)
2092 {
2093 Copy(other);
2094 } // IndexIterator::IndexIterator
2095
operator =(IndexIterator const & rhs)2096 IndexIterator& IndexIterator::operator=(IndexIterator const& rhs)
2097 {
2098 Copy(rhs);
2099 return (*this);
2100 } // IndexIterator::operator=
2101
Copy(IndexIterator const & other)2102 void IndexIterator::Copy(IndexIterator const& other)
2103 {
2104 if (&other != this)
2105 {
2106 m_index = other.m_index;
2107 m_indexData = other.m_indexData;
2108 m_chunkSize = other.m_chunkSize;
2109 m_advance = other.m_advance;
2110 m_curVLR = other.m_curVLR;
2111 m_curCellStartPos = other.m_curCellStartPos;
2112 m_curCellX = other.m_curCellX;
2113 m_curCellY = other.m_curCellY;
2114 m_totalPointsScanned = other.m_totalPointsScanned;
2115 m_ptsScannedCurCell = other.m_ptsScannedCurCell;
2116 m_ptsScannedCurVLR = other.m_ptsScannedCurVLR;
2117 m_conformingPtsFound = other.m_conformingPtsFound;
2118 } // if
2119 } // IndexIterator::Copy
2120
ResetPosition(void)2121 void IndexIterator::ResetPosition(void)
2122 {
2123 m_curVLR = m_curCellStartPos = m_curCellX = m_curCellY = 0;
2124 m_totalPointsScanned = m_ptsScannedCurCell = m_ptsScannedCurVLR = 0;
2125 m_conformingPtsFound = 0;
2126 } // IndexIterator::ResetPosition
2127
operator ()(int32_t n)2128 const std::vector<uint32_t>& IndexIterator::operator()(int32_t n)
2129 {
2130 if (n <= 0)
2131 {
2132 ResetPosition();
2133 m_advance = 1;
2134 } // if
2135 else if ((uint32_t)n < m_conformingPtsFound)
2136 {
2137 ResetPosition();
2138 m_advance = n + 1;
2139 } // if
2140 else
2141 {
2142 m_advance = n - m_conformingPtsFound + 1;
2143 } // else
2144 m_indexData.SetIterator(this);
2145 return (m_index->Filter(m_indexData));
2146 } // IndexIterator::operator++
2147
advance(int32_t n)2148 const std::vector<uint32_t>& IndexIterator::advance(int32_t n)
2149 {
2150 if (n > 0)
2151 --n;
2152 return ((*this)(m_conformingPtsFound + n));
2153 } // IndexIterator::advance
2154
2155 } // namespace liblas
2156
2157