【问题标题】:Further problems in adapting a geometry object model using boost geometry使用 boost 几何调整几何对象模型的进一步问题
【发布时间】:2021-03-05 19:20:38
【问题描述】:

我想将 boost::geometry 算法分别应用于以下由点、多边形(开放或封闭)和多边形域类(具有任意数量的孔)类组成的不可变 2D 模型,如下所示:

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/register/linestring.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/range.hpp>
#include <memory>
#include <vector>

namespace NGeometry {
   class Point2D {
   public:
      Point2D( double x, double y ) : m_x( x ), m_y( y ) {}
      double getX() const { return m_x; }
      double getY() const { return m_y; }
   private:
      double m_x, m_y;
   };
   using TyPoints2D = std::vector< Point2D >;

   // Either open (first point != last point) or closed (first point == last point) polygon
   class Polygon2D {
   public:
      Polygon2D( TyPoints2D points ) : m_points( std::move( points ) ) {}
      const TyPoints2D& getPoints() const { return m_points; }
   private:
      TyPoints2D m_points;
   };
   using TyPolygon2D = std::shared_ptr< Polygon2D >;
   using TyPolygons2D = std::vector< TyPolygon2D >;

   // Polygonal domain with outer cw oriented closed polygon and >= 0 ccw oriented inner polygons
   class PolygonalDomain2D {
   public:
      PolygonalDomain2D( TyPolygon2D outer, TyPolygons2D inners )
         : m_outer( std::move( outer ) ), m_inners( std::move( inners ) ) {}
      const TyPolygon2D& getOuter() const { return m_outer; }
      const TyPolygons2D& getInners() const { return m_inners; }
   private:
      TyPolygon2D m_outer;
      TyPolygons2D m_inners;
   };
} // namespace NGeometry


// Provide read only Boost.Range for Polygon2D
namespace boost {
   template<>
   struct range_const_iterator< NGeometry::Polygon2D >
   {
      typedef std::vector< NGeometry::Point2D >::const_iterator type;
   };
   template<>
   struct range_value< NGeometry::Polygon2D >
   {
      typedef NGeometry::Point2D type;
   };
} // namespace boost
inline std::vector< NGeometry::Point2D >::const_iterator range_begin( const NGeometry::Polygon2D polygon ) {
   return polygon.getPoints().cbegin();
}
inline std::vector< NGeometry::Point2D >::const_iterator range_end( const NGeometry::Polygon2D polygon ) {
   return polygon.getPoints().cend();
}

BOOST_GEOMETRY_REGISTER_POINT_2D_CONST( NGeometry::Point2D, double, cs::cartesian, getX(), getY() );
BOOST_GEOMETRY_REGISTER_LINESTRING( NGeometry::Polygon2D );
// How to register PolygonalDomain2D?

int main( int argc, char* argv[] )
{
   NGeometry::Point2D point( 0.0, 0.0 );
   auto outerPolygonSP = std::make_shared< NGeometry::Polygon2D >(
      NGeometry::TyPoints2D { { -2.0, -2.0 }, { -2.0, 2.0 }, { 2.0, 2.0 }, { 2.0, -2.0 }, { -2.0, -2.0 } } );
   auto innerPolygonSP = std::make_shared< NGeometry::Polygon2D >(
      NGeometry::TyPoints2D { { -1.0, -1.0 }, { 1.0, -1.0 }, { 1.0, 1.0 }, { -1.0, 1.0 }, { -1.0, -1.0 } } );
   NGeometry::PolygonalDomain2D domain( outerPolygonSP, { innerPolygonSP } );

   double length = boost::geometry::length( *outerPolygonSP );
   double area = boost::geometry::area( domain );
   bool isInside = boost::geometry::within( point, domain );

   return 0;
}

Problems in adapting a geometry object model using boost geometry 中描述的第一步相比,类已被修改并移至 NGeometry 命名空间,这会导致编译器错误,因为缺少 NGeometry::Polygon2D 的开始和结束。此外,我不知道如何调整域。如果有任何帮助,我将不胜感激。

g++ 编译器输出:

In file included from /usr/include/boost/geometry/core/closure.hpp:22,
                 from /usr/include/boost/geometry/geometry.hpp:25,
                 from /usr/include/boost/geometry.hpp:17,
                 from main.cpp:1:
/usr/include/boost/geometry/core/point_type.hpp: In instantiation of ‘struct boost::geometry::traits::point_type<NGeometry::PolygonalDomain2D>’:
/usr/include/boost/geometry/core/point_type.hpp:66:17:   required from ‘struct boost::geometry::core_dispatch::point_type<void, NGeometry::PolygonalDomain2D>’
/usr/include/boost/geometry/core/coordinate_system.hpp:58:59:   required from ‘struct boost::geometry::core_dispatch::coordinate_system<void, NGeometry::PolygonalDomain2D>’
/usr/include/boost/geometry/core/coordinate_system.hpp:93:17:   required from ‘struct boost::geometry::coordinate_system<NGeometry::PolygonalDomain2D>’
/usr/include/boost/geometry/core/cs.hpp:244:17:   required from ‘struct boost::geometry::cs_tag<NGeometry::PolygonalDomain2D>’
/usr/include/boost/geometry/strategies/area_result.hpp:59:8:   required from ‘struct boost::geometry::area_result<NGeometry::PolygonalDomain2D, boost::geometry::default_strategy>’
/usr/include/boost/geometry/algorithms/area.hpp:320:1:   required by substitution of ‘template<class Geometry> typename boost::geometry::area_result<Geometry, boost::geometry::default_strategy>::type boost::geometry::area(const Geometry&) [with Geometry = NGeometry::PolygonalDomain2D]’
main.cpp:78:48:   required from here
/usr/include/boost/geometry/core/point_type.hpp:45:5: error: could not convert ‘boost::geometry::traits::point_type<NGeometry::PolygonalDomain2D>::NOT_IMPLEMENTED_FOR_THIS_POINT_TYPE45::assert_arg()’ from ‘mpl_::failed************ (boost::geometry::traits::point_type<NGeometry::PolygonalDomain2D>::NOT_IMPLEMENTED_FOR_THIS_POINT_TYPE::************)(mpl_::assert_::types<NGeometry::PolygonalDomain2D, mpl_::na, mpl_::na, mpl_::na>)’ to ‘mpl_::assert<false>::type’ {aka ‘mpl_::assert<false>’}
   45 |     BOOST_MPL_ASSERT_MSG
      |     ^
      |     |
      |     mpl_::failed************ (boost::geometry::traits::point_type<NGeometry::PolygonalDomain2D>::NOT_IMPLEMENTED_FOR_THIS_POINT_TYPE::************)(mpl_::assert_::types<NGeometry::PolygonalDomain2D, mpl_::na, mpl_::na, mpl_::na>)
In file included from /usr/include/boost/geometry/core/coordinate_dimension.hpp:23,
                 from /usr/include/boost/geometry/geometry.hpp:26,
                 from /usr/include/boost/geometry.hpp:17,
                 from main.cpp:1:
/usr/include/boost/geometry/core/point_type.hpp: In instantiation of ‘struct boost::geometry::core_dispatch::point_type<void, NGeometry::PolygonalDomain2D>’:
/usr/include/boost/geometry/core/coordinate_system.hpp:58:59:   required from ‘struct boost::geometry::core_dispatch::coordinate_system<void, NGeometry::PolygonalDomain2D>’
/usr/include/boost/geometry/core/coordinate_system.hpp:93:17:   required from ‘struct boost::geometry::coordinate_system<NGeometry::PolygonalDomain2D>’
/usr/include/boost/geometry/core/cs.hpp:244:17:   required from ‘struct boost::geometry::cs_tag<NGeometry::PolygonalDomain2D>’
/usr/include/boost/geometry/strategies/area_result.hpp:59:8:   required from ‘struct boost::geometry::area_result<NGeometry::PolygonalDomain2D, boost::geometry::default_strategy>’
/usr/include/boost/geometry/algorithms/area.hpp:320:1:   required by substitution of ‘template<class Geometry> typename boost::geometry::area_result<Geometry, boost::geometry::default_strategy>::type boost::geometry::area(const Geometry&) [with Geometry = NGeometry::PolygonalDomain2D]’
main.cpp:78:48:   required from here
/usr/include/boost/geometry/core/point_type.hpp:66:17: error: no type named ‘type’ in ‘struct boost::geometry::traits::point_type<NGeometry::PolygonalDomain2D>’
   66 |         >::type type;
      |                 ^~~~
main.cpp: In function ‘int main(int, char**)’:
main.cpp:78:48: error: no matching function for call to ‘area(NGeometry::PolygonalDomain2D&)’
   78 |    double area = boost::geometry::area( domain );
      |                                                ^
In file included from /usr/include/boost/geometry/geometry.hpp:52,
                 from /usr/include/boost/geometry.hpp:17,
                 from main.cpp:1:
/usr/include/boost/geometry/algorithms/area.hpp:320:1: note: candidate: ‘template<class Geometry> typename boost::geometry::area_result<Geometry, boost::geometry::default_strategy>::type boost::geometry::area(const Geometry&)’
  320 | area(Geometry const& geometry)
      | ^~~~
/usr/include/boost/geometry/algorithms/area.hpp:320:1: note:   substitution of deduced template arguments resulted in errors seen above
/usr/include/boost/geometry/algorithms/area.hpp:356:1: note: candidate: ‘template<class Geometry, class Strategy> typename boost::geometry::area_result<Geometry, Strategy>::type boost::geometry::area(const Geometry&, const Strategy&)’
  356 | area(Geometry const& geometry, Strategy const& strategy)
      | ^~~~
/usr/include/boost/geometry/algorithms/area.hpp:356:1: note:   template argument deduction/substitution failed:
main.cpp:78:48: note:   candidate expects 2 arguments, 1 provided
   78 |    double area = boost::geometry::area( domain );
      |                                                ^
In file included from /usr/include/boost/geometry/core/closure.hpp:22,
                 from /usr/include/boost/geometry/geometry.hpp:25,
                 from /usr/include/boost/geometry.hpp:17,
                 from main.cpp:1:
/usr/include/boost/geometry/algorithms/not_implemented.hpp: In instantiation of ‘struct boost::geometry::nyi::not_implemented_error<void, void, void>’:
/usr/include/boost/geometry/algorithms/not_implemented.hpp:108:8:   required from ‘struct boost::geometry::not_implemented<void, void, void>’
/usr/include/boost/geometry/geometries/concepts/check.hpp:69:8:   required from ‘struct boost::geometry::dispatch::check<const NGeometry::PolygonalDomain2D, void, true>’
/usr/include/boost/geometry/geometries/concepts/check.hpp:196:8:   required from ‘struct boost::geometry::concepts::detail::checker<const NGeometry::PolygonalDomain2D>’
/usr/include/boost/geometry/geometries/concepts/check.hpp:219:31:   required from ‘void boost::geometry::concepts::check() [with Geometry = const NGeometry::PolygonalDomain2D]’
/usr/include/boost/geometry/algorithms/detail/within/interface.hpp:108:41:   required from ‘static bool boost::geometry::resolve_variant::within<Geometry1, Geometry2>::apply(const Geometry1&, const Geometry2&, const Strategy&) [with Strategy = boost::geometry::default_strategy; Geometry1 = NGeometry::Point2D; Geometry2 = NGeometry::PolygonalDomain2D]’
/usr/include/boost/geometry/algorithms/detail/within/interface.hpp:255:17:   required from ‘bool boost::geometry::within(const Geometry1&, const Geometry2&) [with Geometry1 = NGeometry::Point2D; Geometry2 = NGeometry::PolygonalDomain2D]’
main.cpp:79:59:   required from here
/usr/include/boost/geometry/algorithms/not_implemented.hpp:69:5: error: could not convert ‘boost::geometry::nyi::not_implemented_error<void, void, void>::THIS_OPERATION_IS_NOT_OR_NOT_YET_IMPLEMENTED69::assert_arg()’ from ‘mpl_::failed************ (boost::geometry::nyi::not_implemented_error<void, void, void>::THIS_OPERATION_IS_NOT_OR_NOT_YET_IMPLEMENTED::************)(mpl_::assert_::types<void, void, void, mpl_::na>)’ to ‘mpl_::assert<false>::type’ {aka ‘mpl_::assert<false>’}
   69 |     BOOST_MPL_ASSERT_MSG
      |     ^
      |     |
      |     mpl_::failed************ (boost::geometry::nyi::not_implemented_error<void, void, void>::THIS_OPERATION_IS_NOT_OR_NOT_YET_IMPLEMENTED::************)(mpl_::assert_::types<void, void, void, mpl_::na>)
In file included from /usr/include/boost/geometry/core/coordinate_dimension.hpp:21,
                 from /usr/include/boost/geometry/geometry.hpp:26,
                 from /usr/include/boost/geometry.hpp:17,
                 from main.cpp:1:
/usr/include/boost/geometry/core/coordinate_dimension.hpp: In instantiation of ‘void boost::geometry::assert_dimension_equal() [with G1 = NGeometry::Point2D; G2 = NGeometry::PolygonalDomain2D]’:
/usr/include/boost/geometry/algorithms/detail/within/interface.hpp:109:53:   required from ‘static bool boost::geometry::resolve_variant::within<Geometry1, Geometry2>::apply(const Geometry1&, const Geometry2&, const Strategy&) [with Strategy = boost::geometry::default_strategy; Geometry1 = NGeometry::Point2D; Geometry2 = NGeometry::PolygonalDomain2D]’
/usr/include/boost/geometry/algorithms/detail/within/interface.hpp:255:17:   required from ‘bool boost::geometry::within(const Geometry1&, const Geometry2&) [with Geometry1 = NGeometry::Point2D; Geometry2 = NGeometry::PolygonalDomain2D]’
main.cpp:79:59:   required from here
/usr/include/boost/geometry/core/coordinate_dimension.hpp:122:5: error: no type named ‘type’ in ‘struct boost::geometry::dimension<NGeometry::PolygonalDomain2D>’
  122 |     BOOST_STATIC_ASSERT(( static_cast<size_t>(dimension<G1>::type::value) == static_cast<size_t>(dimension<G2>::type::value) ));
      |     ^~~~~~~~~~~~~~~~~~~
In file included from /usr/include/boost/range/functions.hpp:18,
                 from /usr/include/boost/range/iterator_range_core.hpp:38,
                 from /usr/include/boost/lexical_cast.hpp:30,
                 from /usr/include/boost/math/tools/convert_from_string.hpp:15,
                 from /usr/include/boost/math/constants/constants.hpp:13,
                 from /usr/include/boost/geometry/util/math.hpp:29,
                 from /usr/include/boost/geometry/core/radian_access.hpp:33,
                 from /usr/include/boost/geometry/geometry.hpp:42,
                 from /usr/include/boost/geometry.hpp:17,
                 from main.cpp:1:
/usr/include/boost/range/begin.hpp: In instantiation of ‘constexpr typename boost::range_iterator<T>::type boost::range_detail::range_begin(C&) [with C = const NGeometry::Polygon2D; typename boost::range_iterator<T>::type = __gnu_cxx::__normal_iterator<const NGeometry::Point2D*, std::vector<NGeometry::Point2D> >]’:
/usr/include/boost/range/begin.hpp:119:23:   required from ‘void boost::SinglePassRangeConcept<T>::const_constraints(const Rng&) [with T = const NGeometry::Polygon2D; boost::SinglePassRangeConcept<T>::Rng = const NGeometry::Polygon2D]’
/usr/include/boost/range/concepts.hpp:295:13:   required from ‘boost::SinglePassRangeConcept<T>::~SinglePassRangeConcept() [with T = const NGeometry::Polygon2D]’
/usr/include/boost/range/concepts.hpp:318:12:   required from ‘static void boost::concepts::requirement<boost::concepts::failed************ Model::************>::failed() [with Model = boost::ForwardRangeConcept<const NGeometry::Polygon2D>]’
/usr/include/boost/geometry/geometries/concepts/linestring_concept.hpp:111:5:   required from ‘class boost::geometry::concepts::ConstLinestring<const NGeometry::Polygon2D>’
/usr/include/boost/concept/detail/has_constraints.hpp:32:62:   required by substitution of ‘template<class Model> boost::concepts::detail::yes boost::concepts::detail::has_constraints_(Model*, boost::concepts::detail::wrap_constraints<Model, (& Model::constraints)>*) [with Model = boost::geometry::concepts::ConstLinestring<const NGeometry::Polygon2D>]’
/usr/include/boost/concept/detail/has_constraints.hpp:42:5:   [ skipping 3 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/usr/include/boost/concept_check.hpp:50:7:   required from ‘class boost::geometry::detail::concept_check::check<boost::geometry::concepts::ConstLinestring<const NGeometry::Polygon2D> >’
/usr/include/boost/geometry/geometries/concepts/check.hpp:86:8:   required from ‘struct boost::geometry::dispatch::check<const NGeometry::Polygon2D, boost::geometry::linestring_tag, true>’
/usr/include/boost/geometry/geometries/concepts/check.hpp:196:8:   required from ‘struct boost::geometry::concepts::detail::checker<const NGeometry::Polygon2D>’
/usr/include/boost/geometry/geometries/concepts/check.hpp:219:31:   required from ‘void boost::geometry::concepts::check() [with Geometry = const NGeometry::Polygon2D]’
/usr/include/boost/geometry/algorithms/length.hpp:282:36:   required from ‘typename boost::geometry::default_length_result<Geometry>::type boost::geometry::length(const Geometry&) [with Geometry = NGeometry::Polygon2D; typename boost::geometry::default_length_result<Geometry>::type = long double]’
main.cpp:77:61:   required from here
/usr/include/boost/range/concepts.hpp:301:46:   in ‘constexpr’ expansion of ‘boost::range_adl_barrier::begin<NGeometry::Polygon2D>((* & const_range))’
/usr/include/boost/range/begin.hpp:49:18: error: ‘const class NGeometry::Polygon2D’ has no member named ‘begin’
   49 |         return c.begin();
      |                ~~^~~~~
In file included from /usr/include/boost/range/functions.hpp:19,
                 from /usr/include/boost/range/iterator_range_core.hpp:38,
                 from /usr/include/boost/lexical_cast.hpp:30,
                 from /usr/include/boost/math/tools/convert_from_string.hpp:15,
                 from /usr/include/boost/math/constants/constants.hpp:13,
                 from /usr/include/boost/geometry/util/math.hpp:29,
                 from /usr/include/boost/geometry/core/radian_access.hpp:33,
                 from /usr/include/boost/geometry/geometry.hpp:42,
                 from /usr/include/boost/geometry.hpp:17,
                 from main.cpp:1:
/usr/include/boost/range/end.hpp: In instantiation of ‘constexpr typename boost::range_iterator<T>::type boost::range_detail::range_end(C&) [with C = const NGeometry::Polygon2D; typename boost::range_iterator<T>::type = __gnu_cxx::__normal_iterator<const NGeometry::Point2D*, std::vector<NGeometry::Point2D> >]’:
/usr/include/boost/range/end.hpp:113:21:   required from ‘void boost::SinglePassRangeConcept<T>::const_constraints(const Rng&) [with T = const NGeometry::Polygon2D; boost::SinglePassRangeConcept<T>::Rng = const NGeometry::Polygon2D]’
/usr/include/boost/range/concepts.hpp:295:13:   required from ‘boost::SinglePassRangeConcept<T>::~SinglePassRangeConcept() [with T = const NGeometry::Polygon2D]’
/usr/include/boost/range/concepts.hpp:318:12:   required from ‘static void boost::concepts::requirement<boost::concepts::failed************ Model::************>::failed() [with Model = boost::ForwardRangeConcept<const NGeometry::Polygon2D>]’
/usr/include/boost/geometry/geometries/concepts/linestring_concept.hpp:111:5:   required from ‘class boost::geometry::concepts::ConstLinestring<const NGeometry::Polygon2D>’
/usr/include/boost/concept/detail/has_constraints.hpp:32:62:   required by substitution of ‘template<class Model> boost::concepts::detail::yes boost::concepts::detail::has_constraints_(Model*, boost::concepts::detail::wrap_constraints<Model, (& Model::constraints)>*) [with Model = boost::geometry::concepts::ConstLinestring<const NGeometry::Polygon2D>]’
/usr/include/boost/concept/detail/has_constraints.hpp:42:5:   [ skipping 3 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/usr/include/boost/concept_check.hpp:50:7:   required from ‘class boost::geometry::detail::concept_check::check<boost::geometry::concepts::ConstLinestring<const NGeometry::Polygon2D> >’
/usr/include/boost/geometry/geometries/concepts/check.hpp:86:8:   required from ‘struct boost::geometry::dispatch::check<const NGeometry::Polygon2D, boost::geometry::linestring_tag, true>’
/usr/include/boost/geometry/geometries/concepts/check.hpp:196:8:   required from ‘struct boost::geometry::concepts::detail::checker<const NGeometry::Polygon2D>’
/usr/include/boost/geometry/geometries/concepts/check.hpp:219:31:   required from ‘void boost::geometry::concepts::check() [with Geometry = const NGeometry::Polygon2D]’
/usr/include/boost/geometry/algorithms/length.hpp:282:36:   required from ‘typename boost::geometry::default_length_result<Geometry>::type boost::geometry::length(const Geometry&) [with Geometry = NGeometry::Polygon2D; typename boost::geometry::default_length_result<Geometry>::type = long double]’
main.cpp:77:61:   required from here
/usr/include/boost/range/concepts.hpp:302:44:   in ‘constexpr’ expansion of ‘boost::range_adl_barrier::end<NGeometry::Polygon2D>((* & const_range))’
/usr/include/boost/range/end.hpp:50:22: error: ‘const class NGeometry::Polygon2D’ has no member named ‘end’
   50 |             return c.end();
      |                    ~~^~~

【问题讨论】:

    标签: c++ boost-geometry


    【解决方案1】:

    首先,让我们修复Undefined Behaviour,因为您在range_beginrange_end 中按值获取了参数。这意味着,根据定义,您将迭代器返回到在您使用它们之前已经消失的临时对象中。

    此外,根据 Boost Range 设计启用 ADL,方法是将这些重载放在其范围类型的声明命名空间中:

    namespace NGeometry {
        inline std::vector<NGeometry::Point2D>::const_iterator
        range_begin(NGeometry::Polygon2D const& polygon) {
            return polygon.getPoints().cbegin();
        }
        inline std::vector<NGeometry::Point2D>::const_iterator
        range_end(NGeometry::Polygon2D const& polygon) {
            return polygon.getPoints().cend();
        }
    }
    

    好的,哇。现在它不会在我们运行任何东西时立即崩溃(或者更糟糕的是,不要崩溃并导致巨额法律费用)。

    戒指概念

    接下来,您将多边形类型注册为... LINESTRING。这不起作用,因为它不是多边形概念所需要的:RingPolygon

    解决这个问题

    BOOST_GEOMETRY_REGISTER_RING(NGeometry::Polygon2D)
    

    让你走得更远:

    namespace bg = boost::geometry;
    auto outer =
        std::make_shared<NGeometry::Polygon2D>(NGeometry::TyPoints2D{
            {-2.0, -2.0}, {-2.0, 2.0}, {2.0, 2.0}, {2.0, -2.0}, {-2.0, -2.0}});
    
    std::cout << bg::wkt(*outer) << "\n";
    std::cout << bg::dsv(*outer) << "\n";
    std::cout << "Length: " << bg::length(*outer) << "\n";
    std::cout << "Area: " << bg::area(*outer) << "\n";
    

    打印

    POLYGON((-2 -2,-2 2,2 2,2 -2,-2 -2))
    ((-2, -2), (-2, 2), (2, 2), (2, -2), (-2, -2))
    Length: 0
    Area: 16
    

    扩展到PolygonalDomain2D

    您的“域”是 OGC 所知道的多边形。它有一个外圈和可选的多个内圈。您很幸运,OGC 标准还要求反转内环的点顺序。

    但是,通过不是直接而是通过 shared_ptr 聚合环,您稍微复杂化了。我认为您可能会遇到麻烦,也将 shared_ptr 调整为适当的环。我将在下面向您展示我所做的。

    如何注册多边形

    没有“REGISTER_XXX”功能。您必须通过记录在案的Concept Requirements

    文档并非 100% 与现实同步,我前段时间就发现了这一点:(How to) Create own polygon type in boost geometry and use multi_polygon type with it?

    如果你去写下相关的特征:

    template <> struct tag<NGeometry::PolygonalDomain2D> {
        using type = polygon_tag;
    };
    template <> struct ring_mutable_type<NGeometry::PolygonalDomain2D> {
        using type = NGeometry::Polygon2D;
    };
    template <> struct ring_const_type<NGeometry::PolygonalDomain2D> {
        using type = NGeometry::Polygon2D const;
    };
    template <> struct exterior_ring<NGeometry::PolygonalDomain2D> {
        static decltype(auto) get(NGeometry::PolygonalDomain2D &v) {
            return *v.getOuter();
        }
        static decltype(auto) get(NGeometry::PolygonalDomain2D const &v) {
            return *v.getOuter();
        }
    };
    

    现在它变得越来越辣,因为我们已经到达了您的内部环,它们不是存储为环模型的简单容器,而是存储为指向它们的共享指针的容器。

    我要站在巨人的肩膀上,用boost::adaptors::indirected合成一个隐藏那层间接性的范围。

    template <> struct interior_rings<NGeometry::PolygonalDomain2D> {
        static decltype(auto) get(NGeometry::PolygonalDomain2D &v) {
            return v.getInners() | boost::adaptors::indirected;
        }
        static decltype(auto) get(NGeometry::PolygonalDomain2D const &v) {
            return v.getInners() | boost::adaptors::indirected;
        }
    };
    

    接下来,我重新排序了实现之后的类型特征,这样我就可以使用类型推导而不是拼出实现的类型名称:

    template <> struct interior_mutable_type<NGeometry::PolygonalDomain2D> {
        using type = decltype(interior_rings<NGeometry::PolygonalDomain2D>::get(
            std::declval<NGeometry::PolygonalDomain2D>()));
    };
    template <> struct interior_const_type<NGeometry::PolygonalDomain2D> {
        using type = decltype(interior_rings<NGeometry::PolygonalDomain2D>::get(
            std::declval<NGeometry::PolygonalDomain2D>())) const;
    };
    

    有了这个技巧,我们就可以测试了:

    int main() {
        auto report = [](auto heading, auto &g) {
            std::cout << " == " << heading << " ========================\n";
            check_poly(g);
            std::cout << "WKT:       " << bg::wkt(g)       << "\n";
            std::cout << "DSV:       " << bg::dsv(g)       << "\n";
            std::cout << "Area:      " << bg::area(g)      << "\n";
            std::cout << "Perimeter: " << bg::perimeter(g) << "\n";
    
            if constexpr (std::is_same_v<bg::polygon_tag,
                                         typename bg::traits::tag<
                                             std::decay_t<decltype(g)>>::type>)
            {
                std::cout << "Outer Perimeter: "
                          << bg::perimeter(bg::exterior_ring(g)) << "\n";
            }
        };
    
        auto outer =
            std::make_shared<NGeometry::Polygon2D>(NGeometry::TyPoints2D{
                {-3.0, -3.0}, {-3.0, 3.0}, {3.0, 3.0}, {3.0, -3.0}, {-3.0, -3.0}});
        auto inner =
            std::make_shared<NGeometry::Polygon2D>(NGeometry::TyPoints2D{
                {-2.0, -2.0}, {2.0, -2.0}, {2.0, 2.0}, {-2.0, 2.0}, {-2.0, -2.0}});
    
        NGeometry::PolygonalDomain2D domain(outer, {inner});
    
        report("Outer", *outer);
        report("Inner", *inner);
        report("Domain", domain);
    
        std::cout << " == Within Check ========================\n";
        std::cout << "Point (0,0) within domain? " << std::boolalpha
                  << bg::within(NGeometry::Point2D(0.0, 0.0), domain) << "\n";
    }
    

    打印出来的

     == Outer ========================
    WKT:       POLYGON((-3 -3,-3 3,3 3,3 -3,-3 -3))
    DSV:       ((-3, -3), (-3, 3), (3, 3), (3, -3), (-3, -3))
    Area:      36
    Perimeter: 24
     == Inner ========================
    Warning: Geometry has wrong orientation
    WKT:       POLYGON((-2 -2,2 -2,2 2,-2 2,-2 -2))
    DSV:       ((-2, -2), (2, -2), (2, 2), (-2, 2), (-2, -2))
    Area:      -16
    Perimeter: 16
     == Domain ========================
    WKT:       POLYGON((-3 -3,-3 3,3 3,3 -3,-3 -3),(-2 -2,2 -2,2 2,-2 2,-2 -2))
    DSV:       (((-3, -3), (-3, 3), (3, 3), (3, -3), (-3, -3)), ((-2, -2), (2, -2), (2, 2), (-2, 2
    ), (-2, -2)))
    Area:      20
    Perimeter: 40
    Outer Perimeter: 24
     == Within Check ========================
    Point (0,0) within domain? false
    

    注意

    Warning: Geometry has wrong orientation 正是您想要的,因为内环具有相反的点顺序的概念要求。因此,检查确认了我们希望看到的内容。

    完整演示Live On Coliru

    #include <boost/geometry.hpp>
    #include <boost/geometry/geometries/register/ring.hpp>
    #include <boost/geometry/geometries/register/point.hpp>
    #include <boost/range.hpp>
    #include <boost/range/adaptors.hpp>
    #include <memory>
    #include <vector>
    #include <iostream>
    
    namespace NGeometry {
    class Point2D {
      public:
        Point2D(double x, double y) : m_x(x) , m_y(y) { }
        [[nodiscard]] double getX() const { return m_x; }
        [[nodiscard]] double getY() const { return m_y; }
    
      private:
        double m_x, m_y;
    };
    using TyPoints2D = std::vector<Point2D>;
    
    // Either open (first point != last point) or closed (first point == last
    // point) polygon
    class Polygon2D {
      public:
        Polygon2D(TyPoints2D points) : m_points(std::move(points)) {}
        [[nodiscard]] const TyPoints2D &getPoints() const { return m_points; }
    
      private:
        TyPoints2D m_points;
    };
    
    using SharedPolygon2D = std::shared_ptr<Polygon2D>;
    using TyPolygons2D = std::vector<SharedPolygon2D>;
    
    // Polygonal domain with outer cw oriented closed
    // polygon and >= 0 ccw oriented inner polygons
    class PolygonalDomain2D {
      public:
        PolygonalDomain2D(SharedPolygon2D outer, TyPolygons2D inners)
            : m_outer(std::move(outer)), m_inners(std::move(inners)) {}
        [[nodiscard]] const SharedPolygon2D &getOuter() const { return m_outer; }
        [[nodiscard]] const TyPolygons2D &getInners() const { return m_inners; }
    
      private:
        SharedPolygon2D m_outer;
        TyPolygons2D m_inners;
    };
    } // namespace NGeometry
    
    // Provide read only Boost.Range for Polygon2D
    namespace NGeometry {
        inline std::vector<NGeometry::Point2D>::const_iterator
        range_begin(NGeometry::Polygon2D const& polygon) {
            return polygon.getPoints().cbegin();
        }
        inline std::vector<NGeometry::Point2D>::const_iterator
        range_end(NGeometry::Polygon2D const& polygon) {
            return polygon.getPoints().cend();
        }
    
        inline std::vector<NGeometry::Point2D>::const_iterator
        range_begin(NGeometry::Polygon2D& polygon) {
            return polygon.getPoints().cbegin();
        }
        inline std::vector<NGeometry::Point2D>::const_iterator
        range_end(NGeometry::Polygon2D& polygon) {
            return polygon.getPoints().cend();
        }
    }
    
    namespace boost {
        template <> struct range_iterator<NGeometry::Polygon2D> {
            using type = std::vector<NGeometry::Point2D>::const_iterator;
        };
        template <> struct range_const_iterator<NGeometry::Polygon2D> {
            using type = std::vector<NGeometry::Point2D>::const_iterator;
        };
        template <> struct range_value<NGeometry::Polygon2D> {
            using type = NGeometry::Point2D;
        };
    } // namespace boost
    
    BOOST_GEOMETRY_REGISTER_POINT_2D_CONST(NGeometry::Point2D, double,
                                           cs::cartesian, getX(), getY())
    BOOST_GEOMETRY_REGISTER_RING(NGeometry::Polygon2D)
    
    // How to register PolygonalDomain2D?
    namespace boost::geometry::traits {
        template <> struct tag<NGeometry::PolygonalDomain2D> {
            using type = polygon_tag;
        };
        template <> struct ring_mutable_type<NGeometry::PolygonalDomain2D> {
            using type = NGeometry::Polygon2D;
        };
        template <> struct ring_const_type<NGeometry::PolygonalDomain2D> {
            using type = NGeometry::Polygon2D const;
        };
        template <> struct exterior_ring<NGeometry::PolygonalDomain2D> {
            static decltype(auto) get(NGeometry::PolygonalDomain2D &v) {
                return *v.getOuter();
            }
            static decltype(auto) get(NGeometry::PolygonalDomain2D const &v) {
                return *v.getOuter();
            }
        };
        template <> struct interior_rings<NGeometry::PolygonalDomain2D> {
            static decltype(auto) get(NGeometry::PolygonalDomain2D &v) {
                return v.getInners() | boost::adaptors::indirected;
            }
            static decltype(auto) get(NGeometry::PolygonalDomain2D const &v) {
                return v.getInners() | boost::adaptors::indirected;
            }
        };
        template <> struct interior_mutable_type<NGeometry::PolygonalDomain2D> {
            using type = decltype(interior_rings<NGeometry::PolygonalDomain2D>::get(
                std::declval<NGeometry::PolygonalDomain2D>()));
        };
        template <> struct interior_const_type<NGeometry::PolygonalDomain2D> {
            using type = decltype(interior_rings<NGeometry::PolygonalDomain2D>::get(
                std::declval<NGeometry::PolygonalDomain2D>())) const;
        };
    }
    
    namespace bg = boost::geometry;
    
    template <typename G>
    void check_poly(G& g) {
        if constexpr (1) {
            bg::model::polygon<bg::model::d2::point_xy<double>> copy;
            bg::convert(g, copy);
            std::string reason;
            while (!bg::is_valid(copy, reason)) {
                std::cout << "Warning: " << reason << "\n";
                bg::correct(copy);
            }
        }
    }
    
    int main() {
        auto report = [](auto heading, auto &g) {
            std::cout << " == " << heading << " ========================\n";
            check_poly(g);
            std::cout << "WKT:       " << bg::wkt(g)       << "\n";
            std::cout << "DSV:       " << bg::dsv(g)       << "\n";
            std::cout << "Area:      " << bg::area(g)      << "\n";
            std::cout << "Perimeter: " << bg::perimeter(g) << "\n";
    
            if constexpr (std::is_same_v<bg::polygon_tag,
                                         typename bg::traits::tag<
                                             std::decay_t<decltype(g)>>::type>)
            {
                std::cout << "Outer Perimeter: "
                          << bg::perimeter(bg::exterior_ring(g)) << "\n";
            }
        };
    
        auto outer =
            std::make_shared<NGeometry::Polygon2D>(NGeometry::TyPoints2D{
                {-3.0, -3.0}, {-3.0, 3.0}, {3.0, 3.0}, {3.0, -3.0}, {-3.0, -3.0}});
        auto inner =
            std::make_shared<NGeometry::Polygon2D>(NGeometry::TyPoints2D{
                {-2.0, -2.0}, {2.0, -2.0}, {2.0, 2.0}, {-2.0, 2.0}, {-2.0, -2.0}});
    
        NGeometry::PolygonalDomain2D domain(outer, {inner});
    
        report("Outer", *outer);
        report("Inner", *inner);
        report("Domain", domain);
    
        std::cout << " == Within Check ========================\n";
        std::cout << "Point (0,0) within domain? " << std::boolalpha
                  << bg::within(NGeometry::Point2D(0.0, 0.0), domain) << "\n";
    }
    

    【讨论】:

    • 非常感谢您的详细分析和分步说明!我希望 boost 几何示例是这样制定的。非常适合我学习很多东西并理解所有这些概念。
    • 其他读者注意:将 Polygon2D 注册为 ring 重点关注 PolygonalDomain2D 用例。为了计算多边形的长度,必须使用 BOOST_GEOMETRY_REGISTER_LINESTRING(NGeometry::Polygon2D)。
    • @CppBeginner 请不要那样做。根据定义,多边形 不是 线段,并且没有长度。如果你弄乱了概念分类,你会得到错误的结果或直接Undefined Behaviour。只需使用 bg::perimeter 代替!见扩展report函数Live On Compiler Explorer
    • 您当然可以将直向量注册为线串:BOOST_GEOMETRY_REGISTER_LINESTRING(NGeometry::TyPoints2D) 或直接转换为bg::model::linestring&lt;bg::model::d2::point_xy&lt;double&gt;&gt;godbolt.org/z/Y9nh1E
    猜你喜欢
    • 1970-01-01
    • 2010-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多