如何使用变体创建几何图形

How to create a geometry using a variant

本文关键字:创建 几何图形 何使用      更新时间:2023-10-16

是否可以使用boost::variant定义boost::geometry对象?

此代码无法编译,因为它不喜欢geom::read_wkt()中使用的变体对象。

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/variant/variant.hpp>
#include <boost/variant.hpp>
namespace geom = boost::geometry;
typedef geom::model::d2::point_xy<double> point_type;
typedef geom::model::linestring<point_type> linestring_type;
typedef geom::model::polygon<point_type> polygon_type;
typedef boost::variant
<
point_type,
linestring_type,
polygon_type,
> 
geometryVariant;
int main() {
std::string wkt = "LINESTRING(0 0, 1 1, 2, 2)";
geometryVariant gv;
geom::read_wkt(wkt, gv);
return 0;
}

但是,如果我明确定义linestring_type它工作正常

int main() {
std::string wkt = "LINESTRING(0 0, 1 1, 2, 2)";
linestring_type ls;
geom::read_wkt(wkt, ls);
return 0;
}

你正在与图书馆的设计背道而驰。如果需要运行时多态几何类型,请使用一个。

当然,您可以使用变体在增强几何体之上构建一个。因此,让我们假设您要执行此操作。

与变体一样,要对它们进行一般操作,您需要访问者访问潜在的元素类型:

struct {
using result_type = bool;
template <typename... T>
bool operator()(std::string const& wkt, boost::variant<T...>& geo) const {
return boost::apply_visitor(boost::bind(*this, boost::ref(wkt), _1), geo);
}
template <typename Geo>
bool operator()(std::string const& wkt, Geo& geo) const {
try {
geom::read_wkt(wkt, geo);
return true;
} catch(geom::read_wkt_exception const& cant) {
return false;
}
}
} read_wkt;

现在你可以

住在科里鲁

int main() {
std::string wkt = "LINESTRING(0 0, 1 1, 2, 2)";
geometryVariant gv = linestring_type{};
if (read_wkt(wkt, gv))
std::cout << geom::wkt(gv);
}

印刷:

LINESTRING(0 0,1 1,2 0,2 0)

进一步的想法

这可能不会达到您的预期。如果您只有一个默认构造的变体,它将不起作用,因为访问者将访问当前状态(point_type),并且它会失败。

要实际获取从输入中检测几何类型的动态读取方法,您可以:

geometryVariant read_any_wkt(std::string const& wkt) {
{ linestring_type tmp; if (read_wkt(wkt, tmp)) return tmp; }
{ point_type      tmp; if (read_wkt(wkt, tmp)) return tmp; }
{ polygon_type    tmp; if (read_wkt(wkt, tmp)) return tmp; }
throw geom::read_wkt_exception("read_any_wkt failed", wkt);
}

哪个有效:住在科里鲁

int main() {
for (std::string const wkt : {
"LINESTRING(0 0, 1 1, 2 2)",
"POINT(0 0)",
"POLYGON((0 0, 1 1, 2 2, 0 0))", })
{
{
geometryVariant gv;
if (read_wkt(wkt, gv))
std::cout << "read_wkt: " << geom::wkt(gv) << "n";
}
auto any = read_any_wkt(wkt);
std::cout << "read_any_wkt: " << geom::wkt(any) << "n";
}
}

指纹

read_any_wkt: LINESTRING(0 0,1 1,2 2)
read_wkt: POINT(0 0)
read_any_wkt: POINT(0 0)
read_any_wkt: POLYGON((0 0,1 1,2 2,0 0))

奖励:使通用

可悲的是,这是一个相当多的工作:

Live On Coliru (使用 c++14)

#include <boost/fusion/include/accumulate.hpp>
#include <boost/fusion/include/vector.hpp>
struct read_any_wkt_t {
geometryVariant operator()(std::string const& wkt) const {
geometryVariant output;
call_impl(wkt, output);
return output;
}
private:
template <typename... T>
static void call_impl(std::string const& wkt, boost::variant<T...>& output) {
boost::fusion::vector<T...> candidates;
bool success = boost::fusion::accumulate(candidates, false, [&wkt, &output](bool success, auto candidate) {
if (!success && read_wkt(wkt, candidate)) {
output = candidate;
return true;
}
return success;
});
if (!success) throw geom::read_wkt_exception("read_any_wkt failed", wkt);
}
} read_any_wkt;

打印相同的输出。

奖金

与其盲目地尝试解析 WKT,直到未引发异常,从 WKT 反序列化的更好方法是先实际解析类型 ID 并打开它。

为此,我使用 Boost Spirit X3 绘制了一个示例,将输出切换到与前导类型关键字关联的类型。

解析变得更加简单:

template <typename... T>
static void call_impl(std::string const& wkt, boost::variant<T...>& output) {
static auto const switch_ = gen_switch(output);
if (parse(wkt.begin(), wkt.end(), switch_, output)) {
boost::apply_visitor(boost::bind(read_any_helper{}, boost::ref(wkt), _1), output);
} else {
throw geom::read_wkt_exception("Unregistered type", wkt);
}
}

gen_switch调用仅生成一次包含可支持的几何类型的 Trie。

住在科里鲁

请注意,这增加了一些类型、有效性诊断和自动更正

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/multi_linestring.hpp>
#include <boost/geometry/geometries/multi_polygon.hpp>
#include <iostream>
namespace geom = boost::geometry;
namespace bgm = geom::model;
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/bind.hpp>
#include <boost/variant.hpp>
#include <boost/spirit/home/x3.hpp>
namespace detail {
template <typename Variant> struct read_any_helper {
static Variant call(std::string const& wkt) {
Variant output;
call_impl(wkt, output);
return output;
}
using result_type = void;
template <typename Geo> result_type operator()(std::string const& wkt, Geo& output) const {
geom::read_wkt(wkt, output);
}
private:
template <typename... T>
static void call_impl(std::string const& wkt, boost::variant<T...>& output) {
static auto const switch_ = gen_switch(output);
if (parse(wkt.begin(), wkt.end(), switch_, output)) {
boost::apply_visitor(boost::bind(read_any_helper{}, boost::ref(wkt), _1), output);
} else {
throw geom::read_wkt_exception("Unregistered type", wkt);
}
}
template <typename... T>
static auto gen_switch(boost::variant<T...> const&) {
namespace x3 = boost::spirit::x3;
x3::symbols<Variant> result;
boost::fusion::for_each(boost::fusion::vector<T...>{}, [&result](auto&& seed) {
auto const serialized = boost::lexical_cast<std::string>(geom::wkt(seed));
std::string keyword;
if (x3::parse(serialized.begin(), serialized.end(), +x3::alpha, keyword))
result.add(keyword, std::forward<decltype(seed)>(seed));
else
throw std::logic_error(std::string("registering WKT for ") + typeid(seed).name());
});
result.for_each([](auto& key, auto&&...) {
std::cout << "DEBUG: statically registered support for " << key << " typen";
});
return result;
}
};
}
using point_type = bgm::d2::point_xy<double>;
typedef boost::variant<
point_type,
bgm::linestring<point_type>,
bgm::multi_linestring<bgm::linestring<point_type> >,
bgm::polygon<point_type>,
bgm::multi_polygon<bgm::polygon<point_type> >
> AnyGeo;
template <typename Variant = AnyGeo>
Variant read_any_wkt(std::string const& wkt) {
return detail::read_any_helper<Variant>::call(wkt);
}
int main() {
for (auto wkt : {
"LINESTRING(0 0, 1 1, 2 2)",
"POINT(0 0)",
"POLYGON((0 0, 1 1, 2 2))",
"POLYGON((0 0, 1 1, 2 2, 0 0))",
"MULTIPOLYGON(((0 0, 1 1, 2 2, 1 2, 0 0)))",
}) {
AnyGeo any = read_any_wkt(wkt);
std::cout << "read_any_wkt: " << geom::wkt(any) << "n";
std::string reason;
if (!geom::is_valid(any, reason)) {
std::cout << reason << "n";
geom::correct(any);
std::cout << " -- attempted correction: " << geom::wkt(any) << "n";
}
}
}

印刷

DEBUG: statically registered support for LINESTRING type
DEBUG: statically registered support for MULTILINESTRING type
DEBUG: statically registered support for MULTIPOLYGON type
DEBUG: statically registered support for POINT type
DEBUG: statically registered support for POLYGON type
read_any_wkt: LINESTRING(0 0,1 1,2 2)
read_any_wkt: POINT(0 0)
read_any_wkt: POLYGON((0 0,1 1,2 2,0 0))
Geometry has too few points
-- attempted correction: POLYGON((0 0,1 1,2 2,0 0))
read_any_wkt: POLYGON((0 0,1 1,2 2,0 0))
Geometry has spikes. A spike point was found with apex at (2, 2)
-- attempted correction: POLYGON((0 0,1 1,2 2,0 0))
read_any_wkt: MULTIPOLYGON(((0 0,1 1,2 2,1 2,0 0)))
Geometry has wrong orientation
-- attempted correction: MULTIPOLYGON(((0 0,1 2,2 2,1 1,0 0)))