Program Listing for File point_cloud.hpp

Return to documentation for file (include/beluga_ros/point_cloud.hpp)

// Copyright 2024 Ekumen, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef BELUGA_ROS_POINT_CLOUD_HPP
#define BELUGA_ROS_POINT_CLOUD_HPP

#include <range/v3/view/iota.hpp>

#include <beluga/sensor/data/point_cloud.hpp>
#include <beluga/views/take_evenly.hpp>
#include <beluga_ros/messages.hpp>

#include <sophus/se3.hpp>

#include <Eigen/Dense>

#include "beluga/eigen_compatibility.hpp"

namespace beluga_ros {

template <typename T>
class PointCloud3 : public beluga::BasePointCloud<PointCloud3<T>> {
 public:
  using Scalar = T;

  static_assert(
      std::is_same_v<Scalar, float> || std::is_same_v<Scalar, double>,
      "Pointcloud3 only supports float or double datatype");

  explicit PointCloud3(beluga_ros::msg::PointCloud2ConstSharedPtr cloud, Sophus::SE3d origin = Sophus::SE3d())
      : cloud_(std::move(cloud)), origin_(std::move(origin)) {
    assert(cloud_ != nullptr);
    constexpr uint8_t fieldType = sensor_msgs::typeAsPointFieldType<T>::value;
    // Check if point cloud is 3D
    if (cloud_->fields.size() < 3) {
      throw std::invalid_argument("PointCloud is not 3D");
    }
    // Check point cloud is XYZ... type
    if (cloud_->fields.at(0).name != "x" || cloud_->fields.at(1).name != "y" || cloud_->fields.at(2).name != "z") {
      throw std::invalid_argument("PointCloud not XYZ...");
    }
    // Check XYZ datatype is the same
    if (cloud_->fields.at(0).datatype != fieldType || cloud_->fields.at(1).datatype != fieldType ||
        cloud_->fields.at(2).datatype != fieldType) {
      throw std::invalid_argument("XYZ datatype are not same");
    }
    // Check stride is divisible
    if (cloud_->point_step % sizeof(Scalar) != 0) {
      throw std::invalid_argument("Data is not memory-aligned");
    }
    stride_ = static_cast<int>(cloud_->point_step / sizeof(Scalar));
  }

  [[nodiscard]] const auto& origin() const { return origin_; }

  [[nodiscard]] auto points() const {
    const beluga_ros::msg::PointCloud2ConstIterator<Scalar> iter_points(*cloud_, "x");
    return Eigen::Map<const Eigen::Matrix3X<Scalar>, 0, Eigen::OuterStride<>>(
        &iter_points[0], 3, cloud_->width * cloud_->height, stride_);
  }

 private:
  beluga_ros::msg::PointCloud2ConstSharedPtr cloud_;
  int stride_;
  Sophus::SE3d origin_;
};

}  // namespace beluga_ros

#endif  // BELUGA_ROS_POINT_CLOUD_HPP