Skip to content

Commit

Permalink
add Text Area Description to header reader/writer (#64)
Browse files Browse the repository at this point in the history
* add Text Area Description to header reader/writer

* remove excessive indentation in readheader.cpp (l270-l416)

* remove useless nullterminate

* remove useless nullterminate

* remove useless nullterminate

* remove useless nullterminate

* ensure nulltermination when truncating header text area description

* ensure any text area description is null terminated
  • Loading branch information
floriandeboissieu authored Aug 15, 2023
1 parent 1770bf4 commit b2c6804
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 121 deletions.
256 changes: 135 additions & 121 deletions src/readheader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,146 +267,153 @@ List vlrsreader(LASheader* lasheader)
}
else if ((strcmp(vlr.user_id, "LASF_Spec") == 0) && (vlr.data != 0))
{
if (vlr.record_id == 4) // ExtraBytes
{
lvlrsnames.push_back("Extra_Bytes");
lvlrnames.push_back("Extra Bytes Description");
if (vlr.record_id == 3) // TextArea
{
CHAR* text_area_description = (CHAR*) vlr.data;
lvlr.push_back(std::string(text_area_description, vlr.record_length_after_header));
lvlrnames.push_back("Text Area Description");
lvlrsnames.push_back("TextArea");
}
else if (vlr.record_id == 4) // ExtraBytes
{
lvlrsnames.push_back("Extra_Bytes");
lvlrnames.push_back("Extra Bytes Description");

List ExtraBytes(0);
List ExtraBytesnames(0);

List ExtraBytes(0);
List ExtraBytesnames(0);
for (int j = 0; j < lasheader->number_attributes; j++)
{
LASattribute attemp(lasheader->attributes[j]);

for (int j = 0; j < lasheader->number_attributes; j++)
if (attemp.data_type)
{
LASattribute attemp(lasheader->attributes[j]);
int data_type = ((I32)(attemp.data_type)-1)%10;
//int dim = ((I32)(attemp.data_type)-1)/10+1;

List ExtraByte(0);
List ExtraBytenames(0);
ExtraByte.push_back(((I16*)(attemp.reserved))[0]);
ExtraBytenames.push_back("reserved");
ExtraByte.push_back((I32)(attemp.data_type));
ExtraBytenames.push_back("data_type");
ExtraByte.push_back((I32)(attemp.options));
ExtraBytenames.push_back("options");
ExtraByte.push_back(attemp.name);
ExtraBytenames.push_back("name");

// 2 and 3 dimensional arrays are deprecated in LASlib
// (see https://github.com/LAStools/LAStools/blob/master/LASlib/example/lasexample_write_only_with_extra_bytes.cpp)
double scale = 1.0;
if(attemp.has_scale())
{
scale = attemp.scale[0];
ExtraByte.push_back(scale);
ExtraBytenames.push_back("scale");
}

double offset = 0.0;
if(attemp.has_offset())
{
offset = attemp.offset[0];
ExtraByte.push_back(offset);
ExtraBytenames.push_back("offset");
}

if (attemp.data_type)
if (data_type < 8)
{
int data_type = ((I32)(attemp.data_type)-1)%10;
//int dim = ((I32)(attemp.data_type)-1)/10+1;

List ExtraByte(0);
List ExtraBytenames(0);
ExtraByte.push_back(((I16*)(attemp.reserved))[0]);
ExtraBytenames.push_back("reserved");
ExtraByte.push_back((I32)(attemp.data_type));
ExtraBytenames.push_back("data_type");
ExtraByte.push_back((I32)(attemp.options));
ExtraBytenames.push_back("options");
ExtraByte.push_back(attemp.name);
ExtraBytenames.push_back("name");

// 2 and 3 dimensional arrays are deprecated in LASlib
// (see https://github.com/LAStools/LAStools/blob/master/LASlib/example/lasexample_write_only_with_extra_bytes.cpp)
double scale = 1.0;
if(attemp.has_scale())
I64* temp; // as R does not support long long int it is converted to double

if (attemp.has_no_data())
{
temp = ((I64*)(attemp.no_data));
ExtraByte.push_back(*temp);
ExtraBytenames.push_back("no_data");
}

if (attemp.has_min())
{
temp = ((I64*)(attemp.min));
ExtraByte.push_back(*temp*scale+offset);
ExtraBytenames.push_back("min");
}

if (attemp.has_max())
{
scale = attemp.scale[0];
ExtraByte.push_back(scale);
ExtraBytenames.push_back("scale");
temp = ((I64*)(attemp.max));
ExtraByte.push_back(*temp*scale+offset);
ExtraBytenames.push_back("max");
}
}
else
{
F64* temp;

double offset = 0.0;
if(attemp.has_offset())
if (attemp.has_no_data())
{
offset = attemp.offset[0];
ExtraByte.push_back(offset);
ExtraBytenames.push_back("offset");
temp = ((F64*)(attemp.no_data));
ExtraByte.push_back(*temp*scale+offset);
ExtraBytenames.push_back("no_data");
}

if (data_type < 8)
if (attemp.has_min())
{
I64* temp; // as R does not support long long int it is converted to double

if (attemp.has_no_data())
{
temp = ((I64*)(attemp.no_data));
ExtraByte.push_back(*temp);
ExtraBytenames.push_back("no_data");
}

if (attemp.has_min())
{
temp = ((I64*)(attemp.min));
ExtraByte.push_back(*temp*scale+offset);
ExtraBytenames.push_back("min");
}

if (attemp.has_max())
{
temp = ((I64*)(attemp.max));
ExtraByte.push_back(*temp*scale+offset);
ExtraBytenames.push_back("max");
}
temp = ((F64*)(attemp.min));
ExtraByte.push_back(*temp*scale+offset);
ExtraBytenames.push_back("min");
}
else

if (attemp.has_max())
{
F64* temp;

if (attemp.has_no_data())
{
temp = ((F64*)(attemp.no_data));
ExtraByte.push_back(*temp*scale+offset);
ExtraBytenames.push_back("no_data");
}

if (attemp.has_min())
{
temp = ((F64*)(attemp.min));
ExtraByte.push_back(*temp*scale+offset);
ExtraBytenames.push_back("min");
}

if (attemp.has_max())
{
temp = ((F64*)(attemp.max));
ExtraByte.push_back(*temp*scale+offset);
ExtraBytenames.push_back("max");
}
temp = ((F64*)(attemp.max));
ExtraByte.push_back(*temp*scale+offset);
ExtraBytenames.push_back("max");
}
}

// Fix #53
int len = 0 ; while(len < 32 && attemp.description[len] != '\0') len++;
std::string desc;
desc.resize(len);
memcpy(&desc[0], &attemp.description, len);
// Fix #53
int len = 0 ; while(len < 32 && attemp.description[len] != '\0') len++;
std::string desc;
desc.resize(len);
memcpy(&desc[0], &attemp.description, len);

ExtraByte.push_back(desc);
ExtraBytenames.push_back("description");
ExtraByte.push_back(desc);
ExtraBytenames.push_back("description");

ExtraByte.names() = ExtraBytenames;
ExtraBytes.push_back(ExtraByte);
ExtraBytesnames.push_back(attemp.name);
}
else
{
Rcout << "extra byte " << j << " undocumented: dropped" << std::endl;
}
ExtraByte.names() = ExtraBytenames;
ExtraBytes.push_back(ExtraByte);
ExtraBytesnames.push_back(attemp.name);
}
else
{
Rcout << "extra byte " << j << " undocumented: dropped" << std::endl;
}

ExtraBytes.names() = ExtraBytesnames;
lvlr.push_back(ExtraBytes);
}
else if (vlr.record_id >= 100 && vlr.record_id < 355) // FWF
{
LASvlr_wave_packet_descr* vlr_wave_packet_descr = (LASvlr_wave_packet_descr*) vlr.data;

List FWF = List::create(
Named("Bits per sample") = (U32)(vlr_wave_packet_descr->getBitsPerSample()),
Named("Waveform compression type") = (U32)(vlr_wave_packet_descr->getCompressionType()),
Named("Number of sample") = vlr_wave_packet_descr->getNumberOfSamples(),
Named("Temporal Spacing") = vlr_wave_packet_descr->getTemporalSpacing(),
Named("Digitizer Gain") = vlr_wave_packet_descr->getDigitizerGain(),
Named("Digitizer Offset") = vlr_wave_packet_descr->getDigitizerOffset());

lvlrsnames.push_back("Full WaveForm Description");
lvlrnames.push_back("Full WaveForm");
lvlr.push_back(FWF);
}
else
{
// not supported yet
lvlrsnames.push_back(vlr.user_id);
}

ExtraBytes.names() = ExtraBytesnames;
lvlr.push_back(ExtraBytes);
}
else if (vlr.record_id >= 100 && vlr.record_id < 355) // FWF
{
LASvlr_wave_packet_descr* vlr_wave_packet_descr = (LASvlr_wave_packet_descr*) vlr.data;

List FWF = List::create(
Named("Bits per sample") = (U32)(vlr_wave_packet_descr->getBitsPerSample()),
Named("Waveform compression type") = (U32)(vlr_wave_packet_descr->getCompressionType()),
Named("Number of sample") = vlr_wave_packet_descr->getNumberOfSamples(),
Named("Temporal Spacing") = vlr_wave_packet_descr->getTemporalSpacing(),
Named("Digitizer Gain") = vlr_wave_packet_descr->getDigitizerGain(),
Named("Digitizer Offset") = vlr_wave_packet_descr->getDigitizerOffset());

lvlrsnames.push_back("Full WaveForm Description");
lvlrnames.push_back("Full WaveForm");
lvlr.push_back(FWF);
}
else
{
// not supported yet
lvlrsnames.push_back(vlr.user_id);
}
}
else
{
Expand Down Expand Up @@ -490,7 +497,14 @@ List evlrsreader(LASheader* lasheader)
}
else if ((strcmp(vlr.user_id, "LASF_Spec") == 0) && (vlr.data != 0))
{
if (vlr.record_id == 4) // ExtraBytes
if (vlr.record_id == 3) // TextArea
{
CHAR* text_area_description = (CHAR*) vlr.data;
lvlr.push_back(std::string(text_area_description, vlr.record_length_after_header));
lvlrnames.push_back("Text Area Description");
lvlrsnames.push_back("TextArea");
}
else if (vlr.record_id == 4) // ExtraBytes
{
lvlrsnames.push_back("Extra_Bytes");
lvlrnames.push_back("Extra Bytes Description");
Expand Down
35 changes: 35 additions & 0 deletions src/writeLAS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,41 @@ void C_writer(CharacterVector file, List LASheader, List data)
ExtraByte.start = header.get_attribute_start(ExtraByte.id);
}
}
else if (names[i] == "TextArea")
{
if (vlr.containsElementNamed("Text Area Description"))
{
CharacterVector TEXT = vlr["Text Area Description"];
std::string stext = as<std::string>(TEXT);
CharacterVector desc = vlr["description"];
std::string sdesc = as<std::string>(desc);
I32 stext_size = stext.size();

// truncate strings larger than U16_MAX, and ensure it is null terminated
if ((sizeof(CHAR)*stext_size) >= U16_MAX)
{
stext_size = U16_MAX;
stext[stext_size-1]='\0';
}

// ensure that any text area description is null terminated.
I32 null_terminator = 0;
CHAR* vlr_text_area_desc;
if (stext[stext_size-1] == '\0')
{
vlr_text_area_desc = new CHAR[stext_size];
}
else
{
null_terminator = 1;
vlr_text_area_desc = new CHAR[stext_size+1];
vlr_text_area_desc[stext_size] = '\0';
}
memcpy(vlr_text_area_desc, stext.c_str(), sizeof(CHAR)*stext_size);

header.add_vlr("LASF_Spec", 3, (U16)(sizeof(CHAR)*(stext_size+null_terminator)), (U8*)vlr_text_area_desc, FALSE, sdesc.c_str());
}
}
else
{
}
Expand Down

0 comments on commit b2c6804

Please sign in to comment.