diff --git a/.gitignore b/.gitignore index b5f6a44..b0e7f80 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ tmp __pycache__ ign_pdal_tools.egg-info dist +*.idea* diff --git a/CHANGELOG.md b/CHANGELOG.md index 04278b2..62e1336 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- las_remove_dimension: new tool to remove one or many dimensions + # 1.6.0 - color: choose streams for RGB colorization, and IRC colorization (doc https://geoservices.ign.fr/services-web-experts-ortho) - color: detect white images. diff --git a/pdaltools/las_remove_dimensions.py b/pdaltools/las_remove_dimensions.py new file mode 100644 index 0000000..0063d0a --- /dev/null +++ b/pdaltools/las_remove_dimensions.py @@ -0,0 +1,58 @@ +import argparse +import os + +import pdal +from pdaltools.las_info import get_writer_parameters_from_reader_metadata + +def remove_dimensions_from_las(input_las: str, dimensions: [str], output_las: str): + """ + export new las without some dimensions + """ + pipeline = pdal.Pipeline() | pdal.Reader.las(input_las) + pipeline.execute() + points = pipeline.arrays[0] + input_dimensions = list(points.dtype.fields.keys()) + output_dimensions = [dim for dim in input_dimensions if dim not in dimensions] + points_pruned = points[output_dimensions] + params = get_writer_parameters_from_reader_metadata(pipeline.metadata) + pipeline_end = pdal.Pipeline(arrays=[points_pruned]) + pipeline_end |= pdal.Writer.las(output_las, forward="all", **params) + pipeline_end.execute() + + +def parse_args(): + parser = argparse.ArgumentParser("Remove dimensions from las") + parser.add_argument( + "--input_las", + "-i", + type=str, + required=True, + help="Path to the the las for which the dimensions will be removed", + ) + parser.add_argument( + "--output_las", + "-o", + type=str, + required=False, + help="Path to the the output las ; if none, we replace the input las", + ) + parser.add_argument( + "--dimensions", + "-d", + type=str, + required=True, + nargs="+", + help="The dimension we would like to remove from the point cloud file ; be aware to not remove mandatory " + "dimensions of las" + ) + + return parser.parse_args() + + +if __name__ == "__main__": + args = parse_args() + remove_dimensions_from_las( + input_las=args.input_las, + dimensions=args.dimensions, + output_las=args.input_las if args.output_las is None else args.output_las, + ) diff --git a/test/test_las_remove_dimensions.py b/test/test_las_remove_dimensions.py new file mode 100644 index 0000000..00eb282 --- /dev/null +++ b/test/test_las_remove_dimensions.py @@ -0,0 +1,67 @@ +import tempfile +import pdal +import numpy +import os +import logging +import pytest + +from pdaltools import las_remove_dimensions + +TEST_PATH = os.path.dirname(os.path.abspath(__file__)) +INPUT_DIR = os.path.join(TEST_PATH, "data") + +ini_las = os.path.join(INPUT_DIR, "test_data_77055_627760_LA93_IGN69.laz") +added_dimensions = ["DIM_1", "DIM_2"] + +def get_points(input_las : str): + pipeline_read_ini = pdal.Pipeline() | pdal.Reader.las(input_las) + pipeline_read_ini.execute() + return pipeline_read_ini.arrays[0] + +def append_dimension(input_las : str, output_las : str): + pipeline = pdal.Pipeline() + pipeline |= pdal.Reader.las(input_las) + pipeline |= pdal.Filter.ferry(dimensions="=>" + ", =>".join(added_dimensions)) + pipeline |= pdal.Writer.las(output_las, extra_dims="all", forward="all", ) + pipeline.execute() + + +def test_remove_all_dimension(): + + # get initial data + points_ini = get_points(ini_las) + + with tempfile.NamedTemporaryFile(suffix="_add.las") as tmp_las: + append_dimension(ini_las, tmp_las.name) + with tempfile.NamedTemporaryFile(suffix="_rm.las") as tmp_las_rm: + # remove all dimensions + las_remove_dimensions.remove_dimensions_from_las(tmp_las.name, added_dimensions, tmp_las_rm.name) + points_end = get_points(tmp_las_rm.name) + assert numpy.array_equal(points_ini, points_end) # output data should be the same + + +def test_remove_one_dimension(): + + # get initial data + points_ini = get_points(ini_las) + + with tempfile.NamedTemporaryFile(suffix="_add.las") as tmp_las: + append_dimension(ini_las, tmp_las.name) + with tempfile.NamedTemporaryFile(suffix="_rm.las") as tmp_las_rm: + # remove one dimension + las_remove_dimensions.remove_dimensions_from_las(tmp_las.name, ["DIM_1"], tmp_las_rm.name) + points_end = get_points(tmp_las_rm.name) + + assert list(points_end.dtype.fields.keys()).index("DIM_2") >= 0# should still contains DIM_2 + + with pytest.raises(ValueError): + list(points_end.dtype.fields.keys()).index("DIM_1") # should not have DIM_1 + + with pytest.raises(TypeError): + numpy.array_equal(points_ini, points_end) # output data should not be the same + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + test_remove_all_dimension() + test_remove_one_dimension()