1 /*
2 ===============================================================================
3
4 FILE: lasindex.cpp
5
6 CONTENTS:
7
8 see corresponding header file
9
10 PROGRAMMERS:
11
12 martin.isenburg@rapidlasso.com - http://rapidlasso.com
13
14 COPYRIGHT:
15
16 (c) 2011-2017, martin isenburg, rapidlasso - fast tools to catch reality
17
18 This is free software; you can redistribute and/or modify it under the
19 terms of the GNU Lesser General Licence as published by the Free Software
20 Foundation. See the LICENSE.txt file for more information.
21
22 This software is distributed WITHOUT ANY WARRANTY and without even the
23 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
24
25 CHANGE HISTORY:
26
27 see corresponding header file
28
29 ===============================================================================
30 */
31 #include "lasindex.hpp"
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "lasquadtree.hpp"
38 #include "lasinterval.hpp"
39 #ifdef LASZIPDLL_EXPORTS
40 #include "lasreadpoint.hpp"
41 #else
42 #include "lasreader.hpp"
43 #endif
44 #include "bytestreamin_file.hpp"
45 #include "bytestreamout_file.hpp"
46
47 #ifdef UNORDERED
48 // Figure out whether <unordered_map> is in tr1
49 # ifdef __has_include
50 # if __has_include(<unordered_map>)
51 # include <unordered_map>
52 using namespace std;
53 # define UNORDERED_FOUND
54 # endif
55 # endif
56 # ifdef HAVE_UNORDERED_MAP
57 # include <unordered_map>
58 using namespace std;
59 # elif defined(UNORDERED_FOUND)
60 # include <tr1/unordered_map>
61 using namespace std;
62 using namespace tr1;
63 # endif
64 typedef unordered_map<I32,U32> my_cell_hash;
65 #elif defined(LZ_WIN32_VC6)
66 #include <hash_map>
67 using namespace std;
68 typedef hash_map<I32,U32> my_cell_hash;
69 #else
70 #include <unordered_map>
71 using namespace std;
72 typedef unordered_map<I32, U32> my_cell_hash;
73 #endif
74
LASindex()75 LASindex::LASindex()
76 {
77 spatial = 0;
78 interval = 0;
79 have_interval = FALSE;
80 start = 0;
81 end = 0;
82 full = 0;
83 total = 0;
84 cells = 0;
85 }
86
~LASindex()87 LASindex::~LASindex()
88 {
89 if (spatial) delete spatial;
90 if (interval) delete interval;
91 }
92
prepare(LASquadtree * spatial,I32 threshold)93 void LASindex::prepare(LASquadtree* spatial, I32 threshold)
94 {
95 if (this->spatial) delete this->spatial;
96 this->spatial = spatial;
97 if (this->interval) delete this->interval;
98 this->interval = new LASinterval(threshold);
99 }
100
add(const F64 x,const F64 y,const U32 p_index)101 BOOL LASindex::add(const F64 x, const F64 y, const U32 p_index)
102 {
103 I32 cell = spatial->get_cell_index(x, y);
104 return interval->add(p_index, cell);
105 }
106
complete(U32 minimum_points,I32 maximum_intervals,const BOOL verbose)107 void LASindex::complete(U32 minimum_points, I32 maximum_intervals, const BOOL verbose)
108 {
109 if (verbose)
110 {
111 fprintf(stderr,"before complete %d %d\n", minimum_points, maximum_intervals);
112 print(FALSE);
113 }
114 if (minimum_points)
115 {
116 I32 hash1 = 0;
117 my_cell_hash cell_hash[2];
118 // insert all cells into hash1
119 interval->get_cells();
120 while (interval->has_cells())
121 {
122 cell_hash[hash1][interval->index] = interval->full;
123 }
124 while (cell_hash[hash1].size())
125 {
126 I32 hash2 = (hash1+1)%2;
127 cell_hash[hash2].clear();
128 // coarsen if a coarser cell will still have fewer than minimum_points (and points in all subcells)
129 BOOL coarsened = FALSE;
130 U32 i, full;
131 I32 coarser_index;
132 U32 num_indices;
133 U32 num_filled;
134 I32* indices;
135 my_cell_hash::iterator hash_element_inner;
136 my_cell_hash::iterator hash_element_outer = cell_hash[hash1].begin();
137 while (hash_element_outer != cell_hash[hash1].end())
138 {
139 if ((*hash_element_outer).second)
140 {
141 if (spatial->coarsen((*hash_element_outer).first, &coarser_index, &num_indices, &indices))
142 {
143 full = 0;
144 num_filled = 0;
145 for (i = 0; i < num_indices; i++)
146 {
147 if ((*hash_element_outer).first == indices[i])
148 {
149 hash_element_inner = hash_element_outer;
150 }
151 else
152 {
153 hash_element_inner = cell_hash[hash1].find(indices[i]);
154 }
155 if (hash_element_inner != cell_hash[hash1].end())
156 {
157 full += (*hash_element_inner).second;
158 (*hash_element_inner).second = 0;
159 num_filled++;
160 }
161 }
162 if ((full < minimum_points) && (num_filled == num_indices))
163 {
164 interval->merge_cells(num_indices, indices, coarser_index);
165 coarsened = TRUE;
166 cell_hash[hash2][coarser_index] = full;
167 }
168 }
169 }
170 hash_element_outer++;
171 }
172 if (!coarsened) break;
173 hash1 = (hash1+1)%2;
174 }
175 // tell spatial about the existing cells
176 interval->get_cells();
177 while (interval->has_cells())
178 {
179 spatial->manage_cell(interval->index);
180 }
181 if (verbose)
182 {
183 fprintf(stderr,"after minimum_points %d\n", minimum_points);
184 print(FALSE);
185 }
186 }
187 if (maximum_intervals < 0)
188 {
189 maximum_intervals = -maximum_intervals*interval->get_number_cells();
190 }
191 if (maximum_intervals)
192 {
193 interval->merge_intervals(maximum_intervals, verbose);
194 if (verbose)
195 {
196 fprintf(stderr,"after maximum_intervals %d\n", maximum_intervals);
197 print(FALSE);
198 }
199 }
200 }
201
print(BOOL verbose)202 void LASindex::print(BOOL verbose)
203 {
204 U32 total_cells = 0;
205 U32 total_full = 0;
206 U32 total_total = 0;
207 U32 total_intervals = 0;
208 U32 total_check;
209 U32 intervals;
210 interval->get_cells();
211 while (interval->has_cells())
212 {
213 total_check = 0;
214 intervals = 0;
215 while (interval->has_intervals())
216 {
217 total_check += interval->end-interval->start+1;
218 intervals++;
219 }
220 if (total_check != interval->total)
221 {
222 fprintf(stderr,"ERROR: total_check %d != interval->total %d\n", total_check, interval->total);
223 }
224 if (verbose) fprintf(stderr,"cell %d intervals %d full %d total %d (%.2f)\n", interval->index, intervals, interval->full, interval->total, 100.0f*interval->full/interval->total);
225 total_cells++;
226 total_full += interval->full;
227 total_total += interval->total;
228 total_intervals += intervals;
229 }
230 if (verbose) fprintf(stderr,"total cells/intervals %d/%d full %d (%.2f)\n", total_cells, total_intervals, total_full, 100.0f*total_full/total_total);
231 }
232
get_spatial() const233 LASquadtree* LASindex::get_spatial() const
234 {
235 return spatial;
236 }
237
get_interval() const238 LASinterval* LASindex::get_interval() const
239 {
240 return interval;
241 }
242
intersect_rectangle(const F64 r_min_x,const F64 r_min_y,const F64 r_max_x,const F64 r_max_y)243 BOOL LASindex::intersect_rectangle(const F64 r_min_x, const F64 r_min_y, const F64 r_max_x, const F64 r_max_y)
244 {
245 have_interval = FALSE;
246 cells = spatial->intersect_rectangle(r_min_x, r_min_y, r_max_x, r_max_y);
247 // fprintf(stderr,"%d cells of %g/%g %g/%g intersect rect %g/%g %g/%g\n", num_cells, spatial->get_min_x(), spatial->get_min_y(), spatial->get_max_x(), spatial->get_max_y(), r_min_x, r_min_y, r_max_x, r_max_y);
248 if (cells)
249 return merge_intervals();
250 return FALSE;
251 }
252
intersect_tile(const F32 ll_x,const F32 ll_y,const F32 size)253 BOOL LASindex::intersect_tile(const F32 ll_x, const F32 ll_y, const F32 size)
254 {
255 have_interval = FALSE;
256 cells = spatial->intersect_tile(ll_x, ll_y, size);
257 // fprintf(stderr,"%d cells of %g/%g %g/%g intersect tile %g/%g/%g\n", num_cells, spatial->get_min_x(), spatial->get_min_y(), spatial->get_max_x(), spatial->get_max_y(), ll_x, ll_y, size);
258 if (cells)
259 return merge_intervals();
260 return FALSE;
261 }
262
intersect_circle(const F64 center_x,const F64 center_y,const F64 radius)263 BOOL LASindex::intersect_circle(const F64 center_x, const F64 center_y, const F64 radius)
264 {
265 have_interval = FALSE;
266 cells = spatial->intersect_circle(center_x, center_y, radius);
267 // fprintf(stderr,"%d cells of %g/%g %g/%g intersect circle %g/%g/%g\n", num_cells, spatial->get_min_x(), spatial->get_min_y(), spatial->get_max_x(), spatial->get_max_y(), center_x, center_y, radius);
268 if (cells)
269 return merge_intervals();
270 return FALSE;
271 }
272
get_intervals()273 BOOL LASindex::get_intervals()
274 {
275 have_interval = FALSE;
276 return interval->get_merged_cell();
277 }
278
has_intervals()279 BOOL LASindex::has_intervals()
280 {
281 if (interval->has_intervals())
282 {
283 start = interval->start;
284 end = interval->end;
285 full = interval->full;
286 have_interval = TRUE;
287 return TRUE;
288 }
289 have_interval = FALSE;
290 return FALSE;
291 }
292
read(FILE * file)293 BOOL LASindex::read(FILE* file)
294 {
295 if (file == 0) return FALSE;
296 ByteStreamIn* stream;
297 if (IS_LITTLE_ENDIAN())
298 stream = new ByteStreamInFileLE(file);
299 else
300 stream = new ByteStreamInFileBE(file);
301 if (!read(stream))
302 {
303 delete stream;
304 return FALSE;
305 }
306 delete stream;
307 return TRUE;
308 }
309
write(FILE * file) const310 BOOL LASindex::write(FILE* file) const
311 {
312 if (file == 0) return FALSE;
313 ByteStreamOut* stream;
314 if (IS_LITTLE_ENDIAN())
315 stream = new ByteStreamOutFileLE(file);
316 else
317 stream = new ByteStreamOutFileBE(file);
318 if (!write(stream))
319 {
320 delete stream;
321 return FALSE;
322 }
323 delete stream;
324 return TRUE;
325 }
326
read(const char * file_name)327 BOOL LASindex::read(const char* file_name)
328 {
329 if (file_name == 0) return FALSE;
330 char* name = LASCopyString(file_name);
331 if (strstr(file_name, ".las") || strstr(file_name, ".laz"))
332 {
333 name[strlen(name)-1] = 'x';
334 }
335 else if (strstr(file_name, ".LAS") || strstr(file_name, ".LAZ"))
336 {
337 name[strlen(name)-1] = 'X';
338 }
339 else
340 {
341 name[strlen(name)-3] = 'l';
342 name[strlen(name)-2] = 'a';
343 name[strlen(name)-1] = 'x';
344 }
345 #ifdef _MSC_VER
346 wchar_t* utf16_name = UTF8toUTF16(name);
347 FILE* file = _wfopen(utf16_name, L"rb");
348 delete [] utf16_name;
349 #else
350 FILE* file = fopen(name, "rb");
351 #endif
352 if (file == 0)
353 {
354 free(name);
355 return FALSE;
356 }
357 if (!read(file))
358 {
359 fprintf(stderr,"ERROR (LASindex): cannot read '%s'\n", name);
360 fclose(file);
361 free(name);
362 return FALSE;
363 }
364 fclose(file);
365 free(name);
366 return TRUE;
367 }
368
append(const char * file_name) const369 BOOL LASindex::append(const char* file_name) const
370 {
371 #ifdef LASZIPDLL_EXPORTS
372 return FALSE;
373 #else
374 LASreadOpener lasreadopener;
375
376 if (file_name == 0) return FALSE;
377
378 // open reader
379
380 LASreader* lasreader = lasreadopener.open(file_name);
381 if (lasreader == 0) return FALSE;
382 if (lasreader->header.laszip == 0) return FALSE;
383
384 // close reader
385
386 lasreader->close();
387
388 #ifdef _MSC_VER
389 wchar_t* utf16_file_name = UTF8toUTF16(file_name);
390 FILE* file = _wfopen(utf16_file_name, L"rb");
391 delete [] utf16_file_name;
392 #else
393 FILE* file = fopen(file_name, "rb");
394 #endif
395 ByteStreamIn* bytestreamin = 0;
396 if (IS_LITTLE_ENDIAN())
397 bytestreamin = new ByteStreamInFileLE(file);
398 else
399 bytestreamin = new ByteStreamInFileBE(file);
400
401 // maybe write LASindex EVLR start position into LASzip VLR
402
403 I64 offset_laz_vlr = -1;
404
405 // where to write LASindex EVLR that will contain the LAX file
406
407 I64 number_of_special_evlrs = lasreader->header.laszip->number_of_special_evlrs;
408 I64 offset_to_special_evlrs = lasreader->header.laszip->offset_to_special_evlrs;
409
410 if ((number_of_special_evlrs == -1) && (offset_to_special_evlrs == -1))
411 {
412 bytestreamin->seekEnd();
413 number_of_special_evlrs = 1;
414 offset_to_special_evlrs = bytestreamin->tell();
415
416 // find LASzip VLR
417
418 I64 total = lasreader->header.header_size + 2;
419 U32 number_of_variable_length_records = lasreader->header.number_of_variable_length_records + 1 + (lasreader->header.vlr_lastiling != 0) + (lasreader->header.vlr_lasoriginal != 0);
420
421 for (U32 u = 0; u < number_of_variable_length_records; u++)
422 {
423 bytestreamin->seek(total);
424
425 CHAR user_id[16];
426 try { bytestreamin->getBytes((U8*)user_id, 16); } catch(...)
427 {
428 fprintf(stderr,"ERROR: reading header.vlrs[%d].user_id\n", u);
429 return FALSE;
430 }
431 if (strcmp(user_id, "laszip encoded") == 0)
432 {
433 offset_laz_vlr = bytestreamin->tell() - 18;
434 break;
435 }
436 U16 record_id;
437 try { bytestreamin->get16bitsLE((U8*)&record_id); } catch(...)
438 {
439 fprintf(stderr,"ERROR: reading header.vlrs[%d].record_id\n", u);
440 return FALSE;
441 }
442 U16 record_length_after_header;
443 try { bytestreamin->get16bitsLE((U8*)&record_length_after_header); } catch(...)
444 {
445 fprintf(stderr,"ERROR: reading header.vlrs[%d].record_length_after_header\n", u);
446 return FALSE;
447 }
448 total += (54 + record_length_after_header);
449 }
450
451 if (number_of_special_evlrs == -1) return FALSE;
452 }
453
454 delete bytestreamin;
455 fclose(file);
456
457 ByteStreamOut* bytestreamout;
458 #ifdef _MSC_VER
459 utf16_file_name = UTF8toUTF16(file_name);
460 file = _wfopen(utf16_file_name, L"rb+");
461 delete [] utf16_file_name;
462 #else
463 file = fopen(file_name, "rb+");
464 #endif
465 if (IS_LITTLE_ENDIAN())
466 bytestreamout = new ByteStreamOutFileLE(file);
467 else
468 bytestreamout = new ByteStreamOutFileBE(file);
469 bytestreamout->seek(offset_to_special_evlrs);
470
471 LASevlr lax_evlr;
472 sprintf(lax_evlr.user_id, "LAStools");
473 lax_evlr.record_id = 30;
474 sprintf(lax_evlr.description, "LAX spatial indexing (LASindex)");
475
476 bytestreamout->put16bitsLE((const U8*)&(lax_evlr.reserved));
477 bytestreamout->putBytes((const U8*)lax_evlr.user_id, 16);
478 bytestreamout->put16bitsLE((const U8*)&(lax_evlr.record_id));
479 bytestreamout->put64bitsLE((const U8*)&(lax_evlr.record_length_after_header));
480 bytestreamout->putBytes((const U8*)lax_evlr.description, 32);
481
482 if (!write(bytestreamout))
483 {
484 fprintf(stderr,"ERROR (LASindex): cannot append LAX to '%s'\n", file_name);
485 delete bytestreamout;
486 fclose(file);
487 delete lasreader;
488 return FALSE;
489 }
490
491 // update LASindex EVLR
492
493 lax_evlr.record_length_after_header = bytestreamout->tell() - offset_to_special_evlrs - 60;
494 bytestreamout->seek(offset_to_special_evlrs + 20);
495 bytestreamout->put64bitsLE((const U8*)&(lax_evlr.record_length_after_header));
496
497 // maybe update LASzip VLR
498
499 if (number_of_special_evlrs != -1)
500 {
501 bytestreamout->seek(offset_laz_vlr + 54 + 16);
502 bytestreamout->put64bitsLE((const U8*)&number_of_special_evlrs);
503 bytestreamout->put64bitsLE((const U8*)&offset_to_special_evlrs);
504 }
505
506 // close writer
507
508 bytestreamout->seekEnd();
509 delete bytestreamout;
510 fclose(file);
511
512 // delete reader
513
514 delete lasreader;
515
516 return TRUE;
517 #endif
518 }
519
write(const char * file_name) const520 BOOL LASindex::write(const char* file_name) const
521 {
522 if (file_name == 0) return FALSE;
523 char* name = LASCopyString(file_name);
524 if (strstr(file_name, ".las") || strstr(file_name, ".laz"))
525 {
526 name[strlen(name)-1] = 'x';
527 }
528 else if (strstr(file_name, ".LAS") || strstr(file_name, ".LAZ"))
529 {
530 name[strlen(name)-1] = 'X';
531 }
532 else
533 {
534 name[strlen(name)-3] = 'l';
535 name[strlen(name)-2] = 'a';
536 name[strlen(name)-1] = 'x';
537 }
538 #ifdef _MSC_VER
539 wchar_t* utf16_name = UTF8toUTF16(name);
540 FILE* file = _wfopen(utf16_name, L"wb");
541 delete [] utf16_name;
542 #else
543 FILE* file = fopen(name, "wb");
544 #endif
545 if (file == 0)
546 {
547 fprintf(stderr,"ERROR (LASindex): cannot open '%s' for write\n", name);
548 free(name);
549 return FALSE;
550 }
551 if (!write(file))
552 {
553 fprintf(stderr,"ERROR (LASindex): cannot write '%s'\n", name);
554 fclose(file);
555 free(name);
556 return FALSE;
557 }
558 fclose(file);
559 free(name);
560 return TRUE;
561 }
562
read(ByteStreamIn * stream)563 BOOL LASindex::read(ByteStreamIn* stream)
564 {
565 if (spatial)
566 {
567 delete spatial;
568 spatial = 0;
569 }
570 if (interval)
571 {
572 delete interval;
573 interval = 0;
574 }
575 char signature[4];
576 try { stream->getBytes((U8*)signature, 4); } catch (...)
577 {
578 fprintf(stderr,"ERROR (LASindex): reading signature\n");
579 return FALSE;
580 }
581 if (strncmp(signature, "LASX", 4) != 0)
582 {
583 fprintf(stderr,"ERROR (LASindex): wrong signature %4s instead of 'LASX'\n", signature);
584 return FALSE;
585 }
586 U32 version;
587 try { stream->get32bitsLE((U8*)&version); } catch (...)
588 {
589 fprintf(stderr,"ERROR (LASindex): reading version\n");
590 return FALSE;
591 }
592 // read spatial quadtree
593 spatial = new LASquadtree();
594 if (!spatial->read(stream))
595 {
596 fprintf(stderr,"ERROR (LASindex): cannot read LASspatial (LASquadtree)\n");
597 return FALSE;
598 }
599 // read interval
600 interval = new LASinterval();
601 if (!interval->read(stream))
602 {
603 fprintf(stderr,"ERROR (LASindex): reading LASinterval\n");
604 return FALSE;
605 }
606 // tell spatial about the existing cells
607 interval->get_cells();
608 while (interval->has_cells())
609 {
610 spatial->manage_cell(interval->index);
611 }
612 return TRUE;
613 }
614
write(ByteStreamOut * stream) const615 BOOL LASindex::write(ByteStreamOut* stream) const
616 {
617 if (!stream->putBytes((const U8*)"LASX", 4))
618 {
619 fprintf(stderr,"ERROR (LASindex): writing signature\n");
620 return FALSE;
621 }
622 U32 version = 0;
623 if (!stream->put32bitsLE((const U8*)&version))
624 {
625 fprintf(stderr,"ERROR (LASindex): writing version\n");
626 return FALSE;
627 }
628 // write spatial quadtree
629 if (!spatial->write(stream))
630 {
631 fprintf(stderr,"ERROR (LASindex): cannot write LASspatial (LASquadtree)\n");
632 return FALSE;
633 }
634 // write interval
635 if (!interval->write(stream))
636 {
637 fprintf(stderr,"ERROR (LASindex): writing LASinterval\n");
638 return FALSE;
639 }
640 return TRUE;
641 }
642
643 // seek to next interval point
644
645 #ifdef LASZIPDLL_EXPORTS
seek_next(LASreadPoint * reader,I64 & p_count)646 BOOL LASindex::seek_next(LASreadPoint* reader, I64 &p_count)
647 {
648 if (!have_interval)
649 {
650 if (!has_intervals()) return FALSE;
651 reader->seek((U32)p_count, start);
652 p_count = start;
653 }
654 if (p_count == end)
655 {
656 have_interval = FALSE;
657 }
658 return TRUE;
659 }
660 #else
seek_next(LASreader * lasreader)661 BOOL LASindex::seek_next(LASreader* lasreader)
662 {
663 if (!have_interval)
664 {
665 if (!has_intervals()) return FALSE;
666 lasreader->seek(start);
667 }
668 if (lasreader->p_count == end)
669 {
670 have_interval = FALSE;
671 }
672 return TRUE;
673 }
674 #endif
675
676 // merge the intervals of non-empty cells
merge_intervals()677 BOOL LASindex::merge_intervals()
678 {
679 if (spatial->get_intersected_cells())
680 {
681 U32 used_cells = 0;
682 while (spatial->has_more_cells())
683 {
684 if (interval->get_cell(spatial->current_cell))
685 {
686 interval->add_current_cell_to_merge_cell_set();
687 used_cells++;
688 }
689 }
690 // fprintf(stderr,"LASindex: used %d cells of total %d\n", used_cells, interval->get_number_cells());
691 if (used_cells)
692 {
693 BOOL r = interval->merge();
694 full = interval->full;
695 total = interval->total;
696 interval->clear_merge_cell_set();
697 return r;
698 }
699 }
700 return FALSE;
701 }
702