首先,让我们修复未定义的行为因为你按值获取参数range_begin
and range_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。这是行不通的,因为它不是多边形概念所需要的:Ring and Polygon.
解决这个问题
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";
Prints
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 标准还要求反转内环的点顺序。
然而,你通过聚合环而不是聚合环使事情变得有点复杂化。directly但是通过shared_ptr。我觉得你might还要经历将shared_ptr调整为适当的环的麻烦。下面我将向您展示我所做的事情。
如何注册多边形
没有“REGISTER_XXX”功能。你必须按照记录进行概念要求.
该文档与现实并不 100% 同步,我不久前就发现了这一点:(如何)在 boost 几何体中创建自己的多边形类型并使用 multi_polygon 类型?
如果你去写出相关的特征:
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
Note
The Warning: Geometry has wrong orientation
由于内环具有相反点顺序的概念要求,这正是您想要的。因此,检查证实了我们想要看到的内容。
完整演示住在科里鲁
#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";
}