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

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。请注意,这会添加一些类型、有效性诊断和自动更正

以上是关于如何使用变体创建几何图形的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Python Gremlin 变体中添加边

具有不同变体架构的产品

如何使用 EuCOM 在 Euphoria 中创建 BSTR 的变体数组?

如何在 django rest 框架中仅使用特定变体对象将项目添加到愿望清单?

使用 FFMPEG 创建 HLS 变体

Boost:创建一个返回变体的函数