是否可以使用boost::geometry
定义boost::variant
对象?
此代码无法编译,因为它不喜欢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;
}
答案 0 :(得分:4)
你正在游泳图书馆的设计。如果需要运行时多态几何类型,请使用一个。
当然,您可以使用变体
与变体一样,要对它们进行一般操作,您需要访问者访问潜在的元素类型:
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
)和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绘制了一个示例,将输出切换为与leading类型关键字相关联的类型。
解析变得更简单:
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)))