diff --git a/python/bindings.cpp b/python/bindings.cpp index 4961c924..69260a39 100644 --- a/python/bindings.cpp +++ b/python/bindings.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -24,7 +25,18 @@ PYBIND11_MAKE_OPAQUE(std::vector); PYBIND11_MODULE(copclib, m) { - py::bind_vector>(m, "VectorChar", py::buffer_protocol()); + + py::bind_vector>(m, "VectorChar", py::buffer_protocol()) + .def(py::pickle( + [](const std::vector &vec) { // __getstate__ + // Convert vector to string for pickling + return py::make_tuple(std::string(vec.begin(), vec.end())); + }, + [](const py::tuple &t) { // __setstate__ + auto s = t[0].cast(); + // Convert string back to vector for unpickling + return std::vector(s.begin(), s.end()); + })); py::class_(m, "VoxelKey") .def(py::init<>()) @@ -78,7 +90,15 @@ PYBIND11_MODULE(copclib, m) .def("DefaultOffset", &Vector3::DefaultOffset) .def(py::self == py::self) .def("__str__", &Vector3::ToString) - .def("__repr__", &Vector3::ToString); + .def("__repr__", &Vector3::ToString) + .def(py::pickle( + [](const Vector3 &vec) { // __getstate__ + return py::make_tuple(vec.x, vec.y, vec.z); + }, + [](const py::tuple &t) { // __setstate__ + return Vector3(t[0].cast(), t[1].cast(), t[2].cast()); + })); + py::implicitly_convertible(); py::class_(m, "Point") @@ -279,7 +299,51 @@ PYBIND11_MODULE(copclib, m) .def_readwrite("wave_offset", &las::LasHeader::wave_offset) .def_readwrite("evlr_offset", &las::LasHeader::evlr_offset) .def_readwrite("evlr_count", &las::LasHeader::evlr_count) - .def_readwrite("points_by_return_14", &las::LasHeader::points_by_return_14); + .def_readwrite("point_count_14", &las::LasHeader::point_count) + .def_readwrite("points_by_return_14", &las::LasHeader::points_by_return_14) + .def(py::pickle( + [](const las::LasHeader &h) { // __getstate__ + /* Return a tuple that fully encodes the state of the object */ + return py::make_tuple(h.file_source_id, h.global_encoding, h.GUID(), h.version_major, h.version_minor, + h.SystemIdentifier(), h.GeneratingSoftware(), h.creation_day, h.creation_year, + h.header_size, h.point_offset, h.vlr_count, h.point_format_id, + h.point_record_length, h.point_count, h.points_by_return, h.scale, h.offset, + h.max, h.min, h.wave_offset, h.evlr_offset, h.evlr_count, h.point_count_14, + h.points_by_return_14); + }, + [](py::tuple t) { // __setstate__ + if (t.size() != 25) + throw std::runtime_error("Invalid state!"); + + /* Create a new C++ instance */ + las::LasHeader h; + h.file_source_id = t[0].cast(); + h.global_encoding = t[1].cast(); + h.GUID(t[2].cast()); + h.version_major = t[3].cast(); + h.version_minor = t[4].cast(); + h.SystemIdentifier(t[5].cast()); + h.GeneratingSoftware(t[6].cast()); + h.creation_day = t[7].cast(); + h.creation_year = t[8].cast(); + h.header_size = t[9].cast(); + h.point_offset = t[10].cast(); + h.vlr_count = t[11].cast(); + h.point_format_id = t[12].cast(); + h.point_record_length = t[13].cast(); + h.point_count = t[14].cast(); + h.points_by_return = t[15].cast>(); + h.scale = t[16].cast(); + h.offset = t[17].cast(); + h.max = t[18].cast(); + h.min = t[19].cast(); + h.wave_offset = t[20].cast(); + h.evlr_offset = t[21].cast(); + h.evlr_count = t[22].cast(); + h.point_count_14 = t[23].cast(); + h.points_by_return_14 = t[24].cast>(); + return h; + })); py::class_(m, "LasConfig") .def(py::init(), py::arg("point_format_id"), diff --git a/src/las/header.cpp b/src/las/header.cpp index e8a5df82..67b3fe1c 100644 --- a/src/las/header.cpp +++ b/src/las/header.cpp @@ -44,6 +44,7 @@ LasHeader LasHeader::FromLazPerf(const lazperf::header14 &header) h.wave_offset = header.wave_offset; h.evlr_offset = header.evlr_offset; h.evlr_count = header.evlr_count; + h.point_count_14 = header.point_count_14; std::copy(std::begin(header.points_by_return_14), std::end(header.points_by_return_14), std::begin(h.points_by_return_14)); return h; @@ -86,6 +87,7 @@ lazperf::header14 LasHeader::ToLazPerf() const h.wave_offset = wave_offset; h.evlr_offset = evlr_offset; h.evlr_count = evlr_count; + h.point_count_14 = point_count_14; std::copy(std::begin(points_by_return_14), std::end(points_by_return_14), std::begin(h.points_by_return_14)); return h; diff --git a/test/las_header_test.py b/test/las_header_test.py index 4f22c1ae..c7e68648 100644 --- a/test/las_header_test.py +++ b/test/las_header_test.py @@ -1,3 +1,4 @@ +import pickle import copclib as copc diff --git a/test/pickle_test.py b/test/pickle_test.py new file mode 100644 index 00000000..2eb283c9 --- /dev/null +++ b/test/pickle_test.py @@ -0,0 +1,56 @@ +import pickle +import copclib as copc + + +def test_vector_char(): + char_vec = copc.VectorChar() + char_vec.append("t") + char_vec.append("e") + char_vec.append("s") + char_vec.append("t") + + char_vec_other = pickle.loads(pickle.dumps(char_vec, -1)) + + assert char_vec == char_vec_other + + +def test_vector3(): + vec3 = copc.Vector3(0, 1, 2) + + vec3_other = pickle.loads(pickle.dumps(vec3, -1)) + + assert vec3 == vec3_other + + +def test_las_header(): + + reader = copc.FileReader("../test/data/autzen-classified.copc.laz") + las_header = reader.GetLasHeader() + + las_header_other = pickle.loads(pickle.dumps(las_header, -1)) + + assert las_header.file_source_id == las_header_other.file_source_id + assert las_header.global_encoding == las_header_other.global_encoding + assert las_header.guid == las_header_other.guid + assert las_header.version_major == las_header_other.version_major + assert las_header.version_minor == las_header_other.version_minor + assert las_header.system_identifier == las_header_other.system_identifier + assert las_header.generating_software == las_header_other.generating_software + assert las_header.creation_day == las_header_other.creation_day + assert las_header.creation_year == las_header_other.creation_year + assert las_header.header_size == las_header_other.header_size + assert las_header.point_offset == las_header_other.point_offset + assert las_header.vlr_count == las_header_other.vlr_count + assert las_header.point_format_id == las_header_other.point_format_id + assert las_header.point_record_length == las_header_other.point_record_length + assert las_header.point_count == las_header_other.point_count + assert las_header.points_by_return == las_header_other.points_by_return + assert las_header.scale == las_header_other.scale + assert las_header.offset == las_header_other.offset + assert las_header.max == las_header_other.max + assert las_header.min == las_header_other.min + assert las_header.wave_offset == las_header_other.wave_offset + assert las_header.evlr_offset == las_header_other.evlr_offset + assert las_header.evlr_count == las_header_other.evlr_count + assert las_header.point_count_14 == las_header_other.point_count_14 + assert las_header.points_by_return_14 == las_header_other.points_by_return_14