// This file is part of crownsegmentr, an R package for identifying tree crowns // within 3D point clouds. // // Copyright (C) 2025 Leon Steinmeier, Nikolai Knapp, UFZ Leipzig // Contact: timon.miesner@thuenen.de // // crownsegmentr is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // crownsegmentr is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with crownsegmentr in a file called "COPYING". If not, // see . #include "ams3d.h" #include "spatial.h" namespace ams3d { _Kernel::_Kernel ( const spatial::point_3d_t ¢er, const double crown_diameter_to_tree_height, const double crown_length_to_tree_height, const double crown_diameter_constant, const double crown_length_constant ): _xy_center{ spatial::get_x( center ), spatial::get_y( center ) }, _center_height_initial{ spatial::get_z( center ) }, _radius{ (crown_diameter_to_tree_height * _center_height_initial + crown_diameter_constant )* 0.5 }, _height{ (crown_length_to_tree_height * _center_height_initial + crown_length_constant )* 0.75 }, _half_height { _height * 0.5 }, _half_height_squared{ std::pow( _half_height, 2 ) }, _radius_squared { std::pow(_radius, 2) }, // The kernel is positioned vertically asymmetric around center. _top_height { _center_height_initial + _height * 2.0/3.0 }, _center_height{ _top_height - _height * 0.5 }, _bottom_height{ _top_height - _height } {} _Kernel::_Kernel ( const spatial::point_3d_t ¢er, const spatial::coordinate_t &ground_height_at_center, const double crown_diameter_to_tree_height, const double crown_length_to_tree_height, const double crown_diameter_constant, const double crown_length_constant ): _xy_center{ spatial::get_x( center ), spatial::get_y( center ) }, _center_height_initial{ spatial::get_z( center ) }, _radius { (crown_diameter_to_tree_height * (_center_height_initial - ground_height_at_center) + crown_diameter_constant )* 0.5 }, _height { (crown_length_to_tree_height * (_center_height_initial - ground_height_at_center) + crown_length_constant )* 0.75 }, _half_height { _height * 0.5 }, _half_height_squared{ std::pow( _half_height, 2 ) }, _radius_squared { std::pow(_radius, 2) }, // The kernel is positioned vertically asymmetric around center. _top_height { _center_height_initial + _height * 2.0/3.0 }, _center_height{ _top_height - _height * 0.5 }, _bottom_height{ _top_height - _height } {} std::vector _Kernel::_find_intersecting_points_in ( const spatial::index_for_3d_points_t &point_cloud ) const { return spatial::get_points_intersecting_vertical_cylinder ( point_cloud, _xy_center, _radius, _bottom_height, _top_height ); } spatial::distance_t _Kernel::_calculate_squared_relative_horizontal_distance_of_center_to ( const spatial::point_3d_t &point ) const { return ( std::pow( spatial::get_x( _xy_center ) - spatial::get_x( point ), 2 ) + std::pow( spatial::get_y( _xy_center ) - spatial::get_y( point ), 2 ) ) / _radius_squared; } spatial::distance_t _Kernel::_calculate_squared_relative_vertical_distance_of_center_to ( const spatial::point_3d_t &point ) const { return std::pow( _center_height - spatial::get_z( point ), 2 ) / _half_height_squared; } double _Kernel::_calculate_point_weight_of ( const spatial::point_3d_t &point ) const { return _math_functions::gauss_unsquared ( _calculate_squared_relative_horizontal_distance_of_center_to( point ) ) * _math_functions::epanechnikov_unsquared ( _calculate_squared_relative_vertical_distance_of_center_to( point ) ); } spatial::point_3d_t _Kernel::calculate_centroid_in ( const spatial::index_for_3d_points_t &point_cloud ) const { // Find the points intersecting with the kernel. std::vector points_in_kernel { _find_intersecting_points_in( point_cloud ) }; // If there is only one point in the kernel, directly return it as the // centroid. // (This usually concerns (near-)ground points.) if (points_in_kernel.size() == 1) { return points_in_kernel[0]; } // Set up an array of point weights. std::vector point_weights{}; point_weights.reserve( points_in_kernel.size() ); // Calculate point weights. for (const auto &point_in_kernel : points_in_kernel) { point_weights.push_back ( _calculate_point_weight_of( point_in_kernel ) ); } // Return the weighted mean point of all points in the kernel. return spatial::weighted_mean_of( points_in_kernel, point_weights ); } }