如何使用变体创建几何图形
Posted
技术标签:
【中文标题】如何使用变体创建几何图形【英文标题】:How to create a geometry using a variant 【发布时间】:2017-12-31 06:47:36 【问题描述】:是否可以使用 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;
【问题讨论】:
复制并粘贴错误信息。 【参考方案1】:您正在与图书馆的设计背道而驰。如果您想要运行时多态几何类型,请使用一种。
当然,您可以在 Boost Geometry 之上使用变体构建一个。所以,假设你想这样做。
与变体一样,要对它们进行通用操作,您需要访问者访问潜在的元素类型:
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;
现在可以
Live On Coliru
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
)和it would fail。
要真正获得从输入中检测几何类型的动态读取方法,您可以:
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);
哪个有效:Live On Coliru
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 used)
#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。
Live On Coliru
请注意,这会添加一些类型、有效性诊断和自动更正
#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 << " type\n";
);
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)))
【讨论】:
很棒的作品。我需要一些时间来消化和应用它,但我应该很快就能接受它作为答案。 从 WKT 反序列化的更好方法是首先实际解析类型 id 并打开它:Live On Coliru。请注意,这会添加一些类型、有效性诊断和自动更正以上是关于如何使用变体创建几何图形的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 EuCOM 在 Euphoria 中创建 BSTR 的变体数组?