lasreader_qfit.cpp
Go to the documentation of this file.
1 /*
2 ===============================================================================
3 
4  FILE: lasreader_qfit.cpp
5 
6  CONTENTS:
7 
8  see corresponding header file
9 
10  PROGRAMMERS:
11 
12  martin.isenburg@gmail.com
13 
14  COPYRIGHT:
15 
16  (c) 2007-2011, Martin Isenburg, LASSO - 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 COPYING 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 "lasreader_qfit.hpp"
32 
33 #include "bytestreamin.hpp"
34 #include "bytestreamin_file.hpp"
35 
36 #include <stdlib.h>
37 #include <string.h>
38 
39 #ifdef _WIN32
40 #include <windows.h>
41 #endif
42 
43 BOOL LASreaderQFIT::open(const char* file_name)
44 {
45  if (file_name == 0)
46  {
47  fprintf(stderr,"ERROR: fine name pointer is zero\n");
48  return FALSE;
49  }
50 
51  // open file
52 
53  file = fopen(file_name, "rb");
54  if (file == 0)
55  {
56  fprintf(stderr, "ERROR: cannot open file '%s'\n", file_name);
57  return FALSE;
58  }
59 
60  // create input stream
61 
62  ByteStreamIn* in;
63  if (IS_LITTLE_ENDIAN())
64  in = new ByteStreamInFileLE(file);
65  else
66  in = new ByteStreamInFileBE(file);
67 
68  // clean header
69 
70  header.clean();
71 
72  // set projection
73 
74  LASvlr_key_entry geo_keys[4];
75 
76  // projected coordinates
77  geo_keys[0].key_id = 1024; // GTModelTypeGeoKey
78  geo_keys[0].tiff_tag_location = 0;
79  geo_keys[0].count = 1;
80  geo_keys[0].value_offset = 2; // ModelTypeGeographic
81 
82  // ellipsoid used with latitude/longitude coordinates
83  geo_keys[1].key_id = 2048; // GeographicTypeGeoKey
84  geo_keys[1].tiff_tag_location = 0;
85  geo_keys[1].count = 1;
86  geo_keys[1].value_offset = 4326; // GCS_WGS_84
87 
88  // vertical units
89  geo_keys[2].key_id = 4099; // VerticalUnitsGeoKey
90  geo_keys[2].tiff_tag_location = 0;
91  geo_keys[2].count = 1;
92  geo_keys[2].value_offset = 9001; // meter
93 
94  // vertical datum
95  geo_keys[3].key_id = 4096; // VerticalCSTypeGeoKey
96  geo_keys[3].tiff_tag_location = 0;
97  geo_keys[3].count = 1;
98  geo_keys[3].value_offset = 5030; // VertCS_WGS_84_ellipsoid
99 
100  header.set_geo_keys(4, geo_keys);
101 
102  // maybe set creation date
103 
104 #ifdef _WIN32
105  WIN32_FILE_ATTRIBUTE_DATA attr;
106  SYSTEMTIME creation;
107  GetFileAttributesEx(file_name, GetFileExInfoStandard, &attr);
108  FileTimeToSystemTime(&attr.ftCreationTime, &creation);
109  int startday[13] = {-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
110  header.file_creation_day = startday[creation.wMonth] + creation.wDay;
111  header.file_creation_year = creation.wYear;
112  // leap year handling
113  if ((((creation.wYear)%4) == 0) && (creation.wMonth > 2)) header.file_creation_day++;
114 #else
116  header.file_creation_year = 2011;
117 #endif
118 
119  return open(in);
120 }
121 
123 {
124  U32 i;
125 
126  if (stream == 0)
127  {
128  fprintf(stderr,"ERROR: ByteStreamIn* pointer is zero\n");
129  return FALSE;
130  }
131 
132  this->stream = stream;
133 
134  // read the QFIT header
135 
136  try { stream->get32bitsLE((U8*)&version); } catch(...)
137  {
138  fprintf(stderr,"ERROR: reading QFIT header\n");
139  return FALSE;
140  }
141 
142  // is QFIT file little-endian
143 
144  if (version == 40 || version == 48 || version == 56)
145  {
148  }
149  else
150  {
152  if (version == 40 || version == 48 || version == 56)
153  {
156  }
157  else
158  {
159  fprintf(stderr,"ERROR: corrupt QFIT header.\n");
160  return FALSE;
161  }
162  }
163 
164  // read version bytes until point start offset
165 
166  try { stream->getBytes((U8*)buffer, version); } catch(...)
167  {
168  fprintf(stderr,"ERROR: reading %d bytes until point start offset from QFIT header\n", version);
169  return FALSE;
170  }
171 
172  // read point start offset
173 
174  try { if (little_endian) stream->get32bitsLE((U8*)&offset); else stream->get32bitsBE((U8*)&offset); } catch(...)
175  {
176  fprintf(stderr,"ERROR: reading point start offset from QFIT header\n");
177  return FALSE;
178  }
179 
180  // seek to end of file find out number of points
181 
182  stream->seekEnd();
183  npoints = (stream->tell() - offset) / version;
184 
185  // seek back to start of points
186 
187  stream->seek(offset);
188 
189  // populate the header as much as possible
190 
191  for (i = 0; i < 32; i++)
192  {
193  header.system_identifier[i] = '\0';
194  header.generating_software[i] = '\0';
195  }
196  sprintf(header.system_identifier, "LAStools (c) by Martin Isenburg");
197  sprintf(header.generating_software, "via LASreaderQFIT (%d)", LAS_TOOLS_VERSION);
198 
201 
204 
205  header.x_scale_factor = 0.000001;
206  header.y_scale_factor = 0.000001;
207  header.z_scale_factor = 0.001;
208  header.x_offset = 0;
209  header.y_offset = 0;
210  header.z_offset = 0;
211 
212  LASattribute scan_azimuth(LAS_ATTRIBUTE_I32, "scan azimuth", "Scan Azimuth (degrees X 1,000)");
213  scan_azimuth.set_scale(0.001);
214  scan_azimuth.set_min(0);
215  scan_azimuth.set_max(360000);
216 
217  header.add_extra_attribute(scan_azimuth);
218 
219  LASattribute pitch(LAS_ATTRIBUTE_I32, "pitch", "Pitch (degrees X 1,000)");
220  pitch.set_scale(0.001);
221  pitch.set_min(-90000);
222  pitch.set_max(90000);
223 
225 
226  LASattribute roll(LAS_ATTRIBUTE_I32, "roll", "Roll (degrees X 1,000)");
227  roll.set_scale(0.001);
228  roll.set_min(-90000);
229  roll.set_max(90000);
230 
232 
233  if (version == 48)
234  {
235  LASattribute pulse_width(LAS_ATTRIBUTE_U8, "pulse width", "Pulse Width (digitizer samples)");
236  header.add_extra_attribute(pulse_width);
237  }
238 
240 
241  // set point type
242 
245 
246  // initialize point
247 
249 
250  // initialize extra attribute offsets
251 
255  if (version == 48)
256  {
258  }
259 
260  // set point count to zero
261 
262  p_count = 0;
263 
264  // approximate bounding box init
265 
267 
268  if (!read_point()) return FALSE;
269 
273 
275  {
276  if (!seek(i)) return FALSE;
277  if (!read_point()) return FALSE;
278  }
279 
280  return seek(0);
281 }
282 
284 {
285  if (p_index < npoints)
286  {
287  p_count = p_index;
288  return stream->seek(p_index*version+offset);
289  }
290  return FALSE;
291 }
292 
294 {
295  if (p_count < npoints)
296  {
297  try { stream->getBytes((U8*)buffer, version); } catch(...)
298  {
299  fprintf(stderr,"ERROR: reading QFIT point after %u of %u\n", (U32)p_count, (U32)npoints);
300  return FALSE;
301  }
302 
303  if (endian_swap)
304  {
305  ENDIAN_SWAP_32((U8*)&buffer[0]);
306  ENDIAN_SWAP_32((U8*)&buffer[1]);
307  ENDIAN_SWAP_32((U8*)&buffer[2]);
308  ENDIAN_SWAP_32((U8*)&buffer[3]);
309  ENDIAN_SWAP_32((U8*)&buffer[5]);
310  ENDIAN_SWAP_32((U8*)&buffer[6]);
311  ENDIAN_SWAP_32((U8*)&buffer[7]);
312  ENDIAN_SWAP_32((U8*)&buffer[8]);
313  ENDIAN_SWAP_32((U8*)&buffer[9]);
314  if (version >= 48)
315  {
316  ENDIAN_SWAP_32((U8*)&buffer[10]);
317  ENDIAN_SWAP_32((U8*)&buffer[11]);
318  }
319  if (version >= 56)
320  {
321  ENDIAN_SWAP_32((U8*)&buffer[12]);
322  ENDIAN_SWAP_32((U8*)&buffer[13]);
323  }
324  }
325 
326  point.gps_time = 0.001*buffer[0];
327  point.x = buffer[2];
328  if (point.x > 180000000) point.x -= 360000000; // convert LARGE positive east longitude to negative
329  point.y = buffer[1];
330  point.z = buffer[3];
331  point.intensity = buffer[5];
332  point.scan_angle_rank = I8_CLAMP(I16_QUANTIZE((0.001*buffer[6])-180.0));
333 
338 
339  if (!populated_header)
340  {
342  // update bounding box
349  }
350 
351  p_count++;
352  return TRUE;
353  }
355  return FALSE;
356 }
357 
359 {
360  return stream;
361 }
362 
363 void LASreaderQFIT::close(BOOL close_stream)
364 {
365  if (close_stream)
366  {
367  if (stream)
368  {
369  delete stream;
370  stream = 0;
371  }
372  if (file)
373  {
374  fclose(file);
375  file = 0;
376  }
377  }
378 }
379 
380 BOOL LASreaderQFIT::reopen(const char* file_name)
381 {
382  if (file_name == 0)
383  {
384  fprintf(stderr,"ERROR: fine name pointer is zero\n");
385  return FALSE;
386  }
387 
388  // open file
389 
390  file = fopen(file_name, "rb");
391  if (file == 0)
392  {
393  fprintf(stderr, "ERROR: cannot open file '%s'\n", file_name);
394  return FALSE;
395  }
396 
397  // create input stream
398 
399  if (IS_LITTLE_ENDIAN())
401  else
403 
404  p_count = 0;
405 
406  return stream->seek(offset);
407 }
408 
410 {
411  file = 0;
412  stream = 0;
413  version = 0;
415  endian_swap = FALSE;
416  offset = 0;
419  pitch_array_offset = -1;
420  roll_array_offset = -1;
422 }
423 
425 {
426  if (stream) close();
427 }
428 
429 LASreaderQFITrescale::LASreaderQFITrescale(F64 x_scale_factor, F64 y_scale_factor, F64 z_scale_factor) : LASreaderQFIT()
430 {
431  scale_factor[0] = x_scale_factor;
432  scale_factor[1] = y_scale_factor;
433  scale_factor[2] = z_scale_factor;
434 }
435 
437 {
438  if (!LASreaderQFIT::open(stream)) return FALSE;
439  // do we need to change anything
441  {
443  }
445  {
447  }
449  {
451  }
452  return TRUE;
453 }
454 
456 {
457  this->offset[0] = x_offset;
458  this->offset[1] = y_offset;
459  this->offset[2] = z_offset;
460 }
461 
463 {
464  if (!LASreaderQFIT::open(stream)) return FALSE;
465  // do we need to change anything
466  if (header.x_offset != offset[0])
467  {
468  header.x_offset = offset[0];
469  }
470  if (header.y_offset != offset[1])
471  {
472  header.y_offset = offset[1];
473  }
474  if (header.z_offset != offset[2])
475  {
476  header.z_offset = offset[2];
477  }
478  return TRUE;
479 }
480 
481 LASreaderQFITrescalereoffset::LASreaderQFITrescalereoffset(F64 x_scale_factor, F64 y_scale_factor, F64 z_scale_factor, F64 x_offset, F64 y_offset, F64 z_offset) : LASreaderQFITrescale(x_scale_factor, y_scale_factor, z_scale_factor), LASreaderQFITreoffset(x_offset, y_offset, z_offset)
482 {
483 }
484 
486 {
487  if (!LASreaderQFIT::open(stream)) return FALSE;
488  // do we need to change anything
490  {
492  }
494  {
496  }
498  {
500  }
501  if (header.x_offset != offset[0])
502  {
503  header.x_offset = offset[0];
504  }
505  if (header.y_offset != offset[1])
506  {
507  header.y_offset = offset[1];
508  }
509  if (header.z_offset != offset[2])
510  {
511  header.z_offset = offset[2];
512  }
513  return TRUE;
514 }
LASquantizer::y_offset
F64 y_offset
Definition: lasdefinitions.hpp:96
ByteStreamIn::tell
virtual I64 tell() const =0
LASheader::system_identifier
I8 system_identifier[32]
Definition: lasdefinitions.hpp:964
LASheader::clean
void clean()
Definition: lasdefinitions.hpp:1095
LASheader::min_y
F64 min_y
Definition: lasdefinitions.hpp:978
LASheader::number_of_points_by_return
U32 number_of_points_by_return[5]
Definition: lasdefinitions.hpp:974
ByteStreamIn::get32bitsBE
virtual void get32bitsBE(U8 *bytes)=0
LASreaderQFIT::little_endian
BOOL little_endian
Definition: lasreader_qfit.hpp:65
LASpoint::gps_time
F64 gps_time
Definition: lasdefinitions.hpp:497
LASpoint::scan_angle_rank
I8 scan_angle_rank
Definition: lasdefinitions.hpp:487
LASreader::point
LASpoint point
Definition: lasreader.hpp:53
LASreaderQFITrescalereoffset::LASreaderQFITrescalereoffset
LASreaderQFITrescalereoffset(F64 x_scale_factor, F64 y_scale_factor, F64 z_scale_factor, F64 x_offset, F64 y_offset, F64 z_offset)
Definition: lasreader_qfit.cpp:481
LASreaderQFITreoffset::offset
F64 offset[3]
Definition: lasreader_qfit.hpp:92
LASreaderQFIT
Definition: lasreader_qfit.hpp:40
LASreaderQFIT::endian_swap
BOOL endian_swap
Definition: lasreader_qfit.hpp:66
LASvlr_key_entry::value_offset
U16 value_offset
Definition: lasdefinitions.hpp:907
ByteStreamIn::seekEnd
virtual BOOL seekEnd(const I64 distance=0)=0
LASheader::generating_software
I8 generating_software[32]
Definition: lasdefinitions.hpp:965
LASreaderQFIT::offset
I32 offset
Definition: lasreader_qfit.hpp:67
LASattribute::set_min
void set_min(U8 *min, I32 dim=0)
Definition: lasdefinitions.hpp:186
I64
long long I64
Definition: mydefs.hpp:48
LASattributer::get_extra_attribute_array_offset
I32 get_extra_attribute_array_offset(const char *name) const
Definition: lasdefinitions.hpp:403
F64
double F64
Definition: mydefs.hpp:52
LASquantizer::x_scale_factor
F64 x_scale_factor
Definition: lasdefinitions.hpp:92
I32
int I32
Definition: mydefs.hpp:35
lasreader_qfit.hpp
LASvlr_key_entry
Definition: lasdefinitions.hpp:901
TRUE
#define TRUE
Definition: mydefs.hpp:137
LASreader::p_count
I64 p_count
Definition: lasreader.hpp:56
LASreader::npoints
I64 npoints
Definition: lasreader.hpp:55
LASheader::extended_number_of_points_by_return
U64 extended_number_of_points_by_return[15]
Definition: lasdefinitions.hpp:989
LASreaderQFITrescale::open
virtual BOOL open(ByteStreamIn *stream)
Definition: lasreader_qfit.cpp:436
LASquantizer::x_offset
F64 x_offset
Definition: lasdefinitions.hpp:95
LASheader::number_of_point_records
U32 number_of_point_records
Definition: lasdefinitions.hpp:973
LASreaderQFITrescale
Definition: lasreader_qfit.hpp:76
I16_QUANTIZE
#define I16_QUANTIZE(n)
Definition: mydefs.hpp:108
LASreaderQFIT::~LASreaderQFIT
virtual ~LASreaderQFIT()
Definition: lasreader_qfit.cpp:424
LASreaderQFITreoffset::open
virtual BOOL open(ByteStreamIn *stream)
Definition: lasreader_qfit.cpp:462
LASreaderQFIT::read_point_default
BOOL read_point_default()
Definition: lasreader_qfit.cpp:293
LASpoint::y
I32 y
Definition: lasdefinitions.hpp:479
LASreaderQFITrescalereoffset::open
BOOL open(ByteStreamIn *stream)
Definition: lasreader_qfit.cpp:485
LASreaderQFIT::reopen
BOOL reopen(const char *file_name)
Definition: lasreader_qfit.cpp:380
LASattribute::set_max
void set_max(U8 *max, I32 dim=0)
Definition: lasdefinitions.hpp:199
ByteStreamIn::seek
virtual BOOL seek(const I64 position)=0
LASvlr_key_entry::tiff_tag_location
U16 tiff_tag_location
Definition: lasdefinitions.hpp:905
LASreaderQFIT::populated_header
BOOL populated_header
Definition: lasreader_qfit.hpp:69
LASheader::point_data_format
U8 point_data_format
Definition: lasdefinitions.hpp:971
LASreaderQFIT::close
void close(BOOL close_stream=TRUE)
Definition: lasreader_qfit.cpp:363
bytestreamin.hpp
LASheader::extended_number_of_point_records
U64 extended_number_of_point_records
Definition: lasdefinitions.hpp:988
LASreaderQFIT::seek
BOOL seek(const I64 p_index)
Definition: lasreader_qfit.cpp:283
LASheader::file_creation_year
U16 file_creation_year
Definition: lasdefinitions.hpp:967
ByteStreamIn::get32bitsLE
virtual void get32bitsLE(U8 *bytes)=0
LASattributer::add_extra_attribute
I32 add_extra_attribute(const LASattribute extra_attribute)
Definition: lasdefinitions.hpp:356
LASpoint::set_extra_attribute
void set_extra_attribute(I32 index, const U8 *data)
Definition: lasdefinitions.hpp:847
LASpoint::get_y
F64 get_y() const
Definition: lasdefinitions.hpp:812
LASreaderQFITrescale::LASreaderQFITrescale
LASreaderQFITrescale(F64 x_scale_factor, F64 y_scale_factor, F64 z_scale_factor)
Definition: lasreader_qfit.cpp:429
LASheader::max_x
F64 max_x
Definition: lasdefinitions.hpp:975
LASpoint::attributer
const LASattributer * attributer
Definition: lasdefinitions.hpp:509
LASreaderQFIT::buffer
I32 buffer[14]
Definition: lasreader_qfit.hpp:68
LASpoint::coordinates
F64 coordinates[3]
Definition: lasdefinitions.hpp:505
LASvlr_key_entry::count
U16 count
Definition: lasdefinitions.hpp:906
LASreaderQFIT::LASreaderQFIT
LASreaderQFIT()
Definition: lasreader_qfit.cpp:409
ByteStreamIn
Definition: bytestreamin.hpp:36
LASheader::update_extra_bytes_vlr
void update_extra_bytes_vlr()
Definition: lasdefinitions.hpp:1296
LASreaderQFITrescale::scale_factor
F64 scale_factor[3]
Definition: lasreader_qfit.hpp:83
LASquantizer::z_offset
F64 z_offset
Definition: lasdefinitions.hpp:97
LASattribute::set_scale
BOOL set_scale(F64 scale, I32 dim=0)
Definition: lasdefinitions.hpp:212
ENDIAN_SWAP_32
void ENDIAN_SWAP_32(U8 *field)
Definition: mydefs.hpp:167
LASreaderQFIT::file
FILE * file
Definition: lasreader_qfit.hpp:62
U8
unsigned char U8
Definition: mydefs.hpp:41
LASvlr_key_entry::key_id
U16 key_id
Definition: lasdefinitions.hpp:904
BOOL
int BOOL
Definition: mydefs.hpp:57
LASpoint::z
I32 z
Definition: lasdefinitions.hpp:480
I8_CLAMP
#define I8_CLAMP(n)
Definition: mydefs.hpp:96
LAS_ATTRIBUTE_I32
#define LAS_ATTRIBUTE_I32
Definition: lasdefinitions.hpp:134
LASheader::set_geo_keys
void set_geo_keys(const I32 number_of_keys, const LASvlr_key_entry *geo_keys)
Definition: lasdefinitions.hpp:1259
FALSE
#define FALSE
Definition: mydefs.hpp:133
ByteStreamInFileLE
Definition: bytestreamin_file.hpp:65
LASreaderQFIT::pulse_width_array_offset
I32 pulse_width_array_offset
Definition: lasreader_qfit.hpp:73
LASheader::max_y
F64 max_y
Definition: lasdefinitions.hpp:977
bytestreamin_file.hpp
LAS_TOOLS_VERSION
#define LAS_TOOLS_VERSION
Definition: lasdefinitions.hpp:47
LASreaderQFITreoffset
Definition: lasreader_qfit.hpp:86
LASheader::min_x
F64 min_x
Definition: lasdefinitions.hpp:976
LAS_ATTRIBUTE_U8
#define LAS_ATTRIBUTE_U8
Definition: lasdefinitions.hpp:129
LASreaderQFIT::get_stream
ByteStreamIn * get_stream() const
Definition: lasreader_qfit.cpp:358
LASpoint::init
BOOL init(const LASquantizer *quantizer, const U8 point_type, const U16 point_size, const LASattributer *attributer=0)
Definition: lasdefinitions.hpp:595
LASreaderQFIT::version
I32 version
Definition: lasreader_qfit.hpp:64
LASreaderQFIT::pitch_array_offset
I32 pitch_array_offset
Definition: lasreader_qfit.hpp:71
LASreader::header
LASheader header
Definition: lasreader.hpp:52
IS_LITTLE_ENDIAN
BOOL IS_LITTLE_ENDIAN()
Definition: mydefs.hpp:144
LASreaderQFIT::open
BOOL open(const char *file_name)
Definition: lasreader_qfit.cpp:43
U32
unsigned int U32
Definition: mydefs.hpp:39
LASattribute
Definition: lasdefinitions.hpp:140
LASreaderQFITreoffset::LASreaderQFITreoffset
LASreaderQFITreoffset(F64 x_offset, F64 y_offset, F64 z_offset)
Definition: lasreader_qfit.cpp:455
LASheader::file_creation_day
U16 file_creation_day
Definition: lasdefinitions.hpp:966
LASattributer::get_total_extra_attributes_size
I16 get_total_extra_attributes_size() const
Definition: lasdefinitions.hpp:385
LASpoint::x
I32 x
Definition: lasdefinitions.hpp:478
LASheader::max_z
F64 max_z
Definition: lasdefinitions.hpp:979
LASpoint::get_z
F64 get_z() const
Definition: lasdefinitions.hpp:813
LASpoint::get_x
F64 get_x() const
Definition: lasdefinitions.hpp:811
LASpoint::compute_coordinates
void compute_coordinates()
Definition: lasdefinitions.hpp:819
LASreader::read_point
BOOL read_point()
Definition: lasreader.hpp:74
ByteStreamIn::getBytes
virtual void getBytes(U8 *bytes, const U32 num_bytes)=0
LASheader::point_data_record_length
U16 point_data_record_length
Definition: lasdefinitions.hpp:972
LASreaderQFIT::roll_array_offset
I32 roll_array_offset
Definition: lasreader_qfit.hpp:72
LASreaderQFIT::scan_azimuth_array_offset
I32 scan_azimuth_array_offset
Definition: lasreader_qfit.hpp:70
LASquantizer::y_scale_factor
F64 y_scale_factor
Definition: lasdefinitions.hpp:93
LASquantizer::z_scale_factor
F64 z_scale_factor
Definition: lasdefinitions.hpp:94
LASreaderQFIT::stream
ByteStreamIn * stream
Definition: lasreader_qfit.hpp:63
LASheader::min_z
F64 min_z
Definition: lasdefinitions.hpp:980
LASpoint::intensity
U16 intensity
Definition: lasdefinitions.hpp:481
ByteStreamInFileBE
Definition: bytestreamin_file.hpp:85


lvr2
Author(s): Thomas Wiemann , Sebastian Pütz , Alexander Mock , Lars Kiesow , Lukas Kalbertodt , Tristan Igelbrink , Johan M. von Behren , Dominik Feldschnieders , Alexander Löhr
autogenerated on Wed Mar 2 2022 00:37:23