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