Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions docs/source/mp2p_icp_filters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -873,38 +873,38 @@ This is typically used to separate **static** (high occupancy) and **dynamic** (
Filter: `FilterRemovePointCloudField`
--------------------------------------

**Description**: Unregisters (removes) a custom point cloud field from a ``CGenericPointsMap`` layer.
**Description**: Unregisters (removes) one or more custom point cloud fields from a ``CGenericPointsMap`` layer.

This filter completely removes a previously registered custom field of any supported type (``float``, ``double``, ``uint16_t``, ``uint8_t``) from a point cloud layer. The field and all its associated data are deleted, freeing the memory.
This filter completely removes previously registered custom fields of any supported type (``float``, ``double``, ``uint16_t``, ``uint8_t``) from a point cloud layer. The fields and all their associated data are deleted, freeing the memory.

Unlike clearing data, this operation removes the field definition itself, as if it had never been registered. After removal, attempting to access the field will fail unless it is registered again.
Unlike clearing data, this operation removes the field definitions themselves, as if they had never been registered. After removal, attempting to access the fields will fail unless they are registered again.

**Important**: This filter only works with layers containing ``mrpt::maps::CGenericPointsMap`` or derived classes. Other map types (like ``CSimplePointsMap``) will be silently skipped.

**Parameters**:

* **pointcloud_layer** (:cpp:type:`std::string`, default: `raw`): The point cloud layer to process.

* **field_name** (:cpp:type:`std::string`, required): The name of the custom field to remove (e.g., ``intensity``, ``ring``, ``timestamp_abs``, or any user-defined field name).
* **field_names** (:cpp:type:`std::string` or :cpp:type:`std::vector<std::string>`, required): One or more custom field names to remove (e.g., ``intensity``, ``ring``, ``timestamp_abs``, or any user-defined field names). Can be specified as a single string or as a list/sequence.

* **throw_on_missing_field** (:cpp:type:`bool`, default: `true`): Whether to throw an exception if the specified field does not exist. If ``false``, missing fields are silently ignored.
* **throw_on_missing_field** (:cpp:type:`bool`, default: `true`): Whether to throw an exception if any of the specified fields does not exist. If ``false``, missing fields are silently ignored.

.. code-block:: yaml

filters:
#...
# Remove a timestamp field that's no longer needed
# Remove a single timestamp field that's no longer needed
- class_name: mp2p_icp_filters::FilterRemovePointCloudField
params:
pointcloud_layer: 'raw'
field_name: 'timestamp_abs'
field_names: 'timestamp_abs'
throw_on_missing_field: true

# Silently remove intensity if it exists
# Remove multiple fields at once
- class_name: mp2p_icp_filters::FilterRemovePointCloudField
params:
pointcloud_layer: 'filtered'
field_name: 'intensity'
pointcloud_layer: 'raw'
field_names: ['intensity', 'ring', 'timestamp_abs']
throw_on_missing_field: false

|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ class FilterRemovePointCloudField : public mp2p_icp_filters::FilterBase
/** The point cloud layer to process */
std::string pointcloud_layer = "raw";

/** The name of the field to remove (e.g., "intensity", "ring", "timestamp_abs") */
std::string field_name;
/** One or more field names to remove (e.g., "intensity", "ring", "timestamp_abs") */
std::vector<std::string> field_names;

/** Whether to throw an exception if the field does not exist.
/** Whether to throw an exception if any field does not exist.
* If false, missing fields are silently ignored. */
bool throw_on_missing_field = true;
};
Expand Down
69 changes: 48 additions & 21 deletions mp2p_icp_filters/src/FilterRemovePointCloudField.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,31 @@ using namespace mp2p_icp_filters;
void FilterRemovePointCloudField::Parameters::load_from_yaml(const mrpt::containers::yaml& c)
{
MCP_LOAD_OPT(c, pointcloud_layer);
MCP_LOAD_REQ(c, field_name);
MCP_LOAD_OPT(c, throw_on_missing_field);

ASSERTMSG_(
c.has("field_names"),
"YAML configuration must have an entry `field_names` with a scalar or sequence.");

field_names.clear();

auto cfgIn = c["field_names"];
if (cfgIn.isScalar())
{
field_names.push_back(cfgIn.as<std::string>());
}
else
{
ASSERTMSG_(
cfgIn.isSequence(),
"YAML configuration must have an entry `field_names` with a scalar or sequence.");

for (const auto& s : cfgIn.asSequence())
{
field_names.push_back(s.as<std::string>());
}
}
ASSERT_(!field_names.empty());
}

FilterRemovePointCloudField::FilterRemovePointCloudField()
Expand Down Expand Up @@ -72,27 +95,31 @@ void FilterRemovePointCloudField::filter(mp2p_icp::metric_map_t& inOut) const
return;
}

// Try to unregister the field
const bool removed = pc->unregisterField(params.field_name);

if (!removed && params.throw_on_missing_field)
{
THROW_EXCEPTION(mrpt::format(
"Field '%s' does not exist in layer '%s'", params.field_name.c_str(),
params.pointcloud_layer.c_str()));
}

if (removed)
{
MRPT_LOG_DEBUG_STREAM(
"Removed field '" << params.field_name << "' from layer '" << params.pointcloud_layer
<< "'");
}
else
// Process each field name
for (const auto& field_name : params.field_names)
{
MRPT_LOG_DEBUG_STREAM(
"Field '" << params.field_name << "' not found in layer '" << params.pointcloud_layer
<< "' (ignored)");
// Try to unregister the field
const bool removed = pc->unregisterField(field_name);

if (!removed && params.throw_on_missing_field)
{
THROW_EXCEPTION(mrpt::format(
"Field '%s' does not exist in layer '%s'", field_name.c_str(),
params.pointcloud_layer.c_str()));
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if (removed)
{
MRPT_LOG_DEBUG_STREAM(
"Removed field '" << field_name << "' from layer '" << params.pointcloud_layer
<< "'");
}
else
{
MRPT_LOG_DEBUG_STREAM(
"Field '" << field_name << "' not found in layer '" << params.pointcloud_layer
<< "' (ignored)");
}
}

MRPT_END
Expand Down
60 changes: 51 additions & 9 deletions tests/test-mp2p_FilterRemovePointCloudField.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ void test_remove_field()
FilterRemovePointCloudField filter;
mrpt::containers::yaml p;
p["pointcloud_layer"] = "raw";
p["field_name"] = "custom_field";
p["field_names"] = "custom_field";
filter.initialize(p);

filter.filter(map);
Expand Down Expand Up @@ -80,7 +80,7 @@ void test_remove_field()
FilterRemovePointCloudField filter;
mrpt::containers::yaml p;
p["pointcloud_layer"] = "raw";
p["field_name"] = "timestamp_abs";
p["field_names"] = "timestamp_abs";
filter.initialize(p);

filter.filter(map);
Expand All @@ -107,7 +107,7 @@ void test_remove_field()
FilterRemovePointCloudField filter;
mrpt::containers::yaml p;
p["pointcloud_layer"] = "raw";
p["field_name"] = "ring";
p["field_names"] = "ring";
filter.initialize(p);

filter.filter(map);
Expand All @@ -134,7 +134,7 @@ void test_remove_field()
FilterRemovePointCloudField filter;
mrpt::containers::yaml p;
p["pointcloud_layer"] = "raw";
p["field_name"] = "intensity";
p["field_names"] = "intensity";
filter.initialize(p);

filter.filter(map);
Expand All @@ -156,7 +156,7 @@ void test_remove_field()
FilterRemovePointCloudField filter;
mrpt::containers::yaml p;
p["pointcloud_layer"] = "raw";
p["field_name"] = "non_existent_field";
p["field_names"] = "non_existent_field";
p["throw_on_missing_field"] = true;
filter.initialize(p);

Expand Down Expand Up @@ -187,7 +187,7 @@ void test_remove_field()
FilterRemovePointCloudField filter;
mrpt::containers::yaml p;
p["pointcloud_layer"] = "raw";
p["field_name"] = "non_existent_field";
p["field_names"] = "non_existent_field";
p["throw_on_missing_field"] = false;
filter.initialize(p);

Expand All @@ -205,7 +205,7 @@ void test_remove_field()
FilterRemovePointCloudField filter;
mrpt::containers::yaml p;
p["pointcloud_layer"] = "non_existent_layer";
p["field_name"] = "some_field";
p["field_names"] = "some_field";
filter.initialize(p);

// Should not throw, just log warning and skip
Expand All @@ -226,7 +226,7 @@ void test_remove_field()
FilterRemovePointCloudField filter;
mrpt::containers::yaml p;
p["pointcloud_layer"] = "simple";
p["field_name"] = "some_field";
p["field_names"] = "some_field";
filter.initialize(p);

// Should not throw, just log warning and skip
Expand Down Expand Up @@ -257,7 +257,7 @@ void test_remove_field()
FilterRemovePointCloudField filter;
mrpt::containers::yaml p;
p["pointcloud_layer"] = "layer1";
p["field_name"] = "field_a";
p["field_names"] = "field_a";
filter.initialize(p);

filter.filter(map);
Expand All @@ -267,6 +267,48 @@ void test_remove_field()
ASSERT_(pc2->hasPointField("field_b"));
std::cout << "[Test Passed] Remove field from specific layer" << std::endl;
}

// ---------------------------------------------------------
// Case 10: Remove multiple fields at once
// ---------------------------------------------------------
{
mp2p_icp::metric_map_t map;
auto pc = mrpt::maps::CGenericPointsMap::Create();

// Register multiple fields
ASSERT_(pc->registerField_float("field1"));
ASSERT_(pc->registerField_double("field2"));
ASSERT_(pc->registerField_uint16("field3"));

pc->insertPoint(1.0f, 2.0f, 3.0f);
pc->insertPointField_float("field1", 1.0f);
pc->insertPointField_double("field2", 2.0);
pc->insertPointField_uint16("field3", 3);

ASSERT_(pc->hasPointField("field1"));
ASSERT_(pc->hasPointField("field2"));
ASSERT_(pc->hasPointField("field3"));

map.layers["raw"] = pc;

FilterRemovePointCloudField filter;
mrpt::containers::yaml p;
p["pointcloud_layer"] = "raw";
// Pass as sequence
p["field_names"] = mrpt::containers::yaml::Sequence();
p["field_names"].asSequence().push_back("field1");
p["field_names"].asSequence().push_back("field2");
p["field_names"].asSequence().push_back("field3");
filter.initialize(p);

filter.filter(map);

// All three fields should be removed
ASSERT_(!pc->hasPointField("field1"));
ASSERT_(!pc->hasPointField("field2"));
ASSERT_(!pc->hasPointField("field3"));
std::cout << "[Test Passed] Remove multiple fields at once" << std::endl;
}
}

int main()
Expand Down