boost::geometry简介,真香

概述

我在进行地图方面的编程,代码中好多对图形的处理都是自己实现的,而且相同的图形每个人都有自己的实现版本,实在是混乱,亟需一种标准来统一。
最近在使用boost库(1.78),发现其中有一个geometry(几何)库。便了解了一下,发现真香。该库使用起来很generic(要不自称Generic Geometry Library, GGL),而且符合国际标准OGC(开放地理空间联盟)。总结一句话,boost::geometry兼容OGC标准,并为其空间对象模型的基础几何操作提供2D空间谓词API。怎么,这句话看不懂,看完这篇文章你就懂了。

摘抄下boost::geometry的官方描述

  • Boost.Geometry might be used in all domains where geometry plays a role: mapping and GIS, game development, computer graphics and
    widgets, robotics, astronomy and more. The core is designed to be as
    generic as possible and support those domains. For now, the
    development has been mostly GIS-oriented.
  • conventions and names from one of the OGC standards on geometry and,more specificly, from the OGC Simple Feature Specification

分类

boost::geometry主要分为几何对象,算法,算数运算,io(图形描述)四大类。

几何对象

几何对象,即OGC标准中的空间对象模型,对应到代码中就是数据类型(类)。不知道给类起个啥名字,不要紧,OGC标准中都给你起好了,linestring表示线,polygon表示多边形。不仅boost库中这样命名,GEOS库中也这样命名,其他库也是。有标准就是好,更换库起来也方便。而且人家标准里的空间对象之间的关系,早就帮你构造好了。没用OGC标准前,我自己还实现三角形类,矩形类,但看了标准后才发现,三角形和矩形不都是多边形吗,一个polygon类就可以了,相见恨晚。用了boost::geometry后才明白,什么是抽象,什么是generic。好了,让我来介绍一下一些基本的空间对象模型。

  • model::point
    点。该点可以是二维/三维的,int/double,cartesian(笛卡尔)/spherical_equatorial(球体)坐标系,度量单位可以是距离/角度。总之,很generic。可以直接用作地图经纬度坐标。

  • model::segment
    线段,两个点构成的线段。用distance算法,算点到线段的距离,得到的是点到线段各点的最短距离,而非点到直线的距离。

  • model::linestring
    线,点集,各点不必在同一直线上,可以用作地图中的形状点,非闭合的线。用distance算法,算点到linestring的距离,得到的是点到各segment的最短距离。

  • model::multi_linestring
    多条linestring,就像地图中的车道线。

  • model::box
    AABB,即axis-aligned bounding box

  • model::polygon
    多边形。

算法

几何图形空间关系非常复杂,人们总结出了DE-9IM模型(九交模型),作为一种空间关系判断的标准。在此基础之上有了谓词的概念,来描述两个几何图形的空间关系,比如包含(within),接触(touches)、重叠(overlaps)等。

  • within
    一个图形是否在另一个图形内部。
  • overlaps
    两个图形是否重叠
  • touches
    两个图形是否仅边界有交集
  • envelope
    获取几何图形的外接矩形AABB
  • distance
    距离,可以是点到点,点到线。

算数运算

  • subtract_point
    两个点做减法操作

  • dot_product
    向量的点乘操作

  • cross_product
    向量的叉乘操作

io(图形描述)

写接口时需要传送图形数据,那怎样将图形数据序列化呢,不用自己定义格式了,有标准滴。
WKT(Well-Known Text)
该格式由OGC制定。比如有个矩形四个顶点的坐标为(0,0)(1,0)(1,1)(0,1),转换成wkt格式则为(0 0,1 0,1 1,0 1)。

一条线可以表示为LINESTRING(1 2,10 50,20 25)。

DSV (Delimiter-Separated Values)
在boost中可以自定义序列化后的格式,wkt不符合你的需求时,可以用这个自定义,灵活性很大。

例子

下载下来的boost库中有使用示例,boost::geometry的使用示例在boost_1_78_0\libs\geometry\example中。没事的话可以多看看,里面的门道很多。
让我举几个例子来说明boost::geometry的强大和generic。

  • 求地理距离

    #include 
    #include 
    namespace bg = boost::geometry;
    int main(int argc, char *argv[])
    {
    	using geo_point = bg::model::point<
    		double, 2, bg::cs::geographic<bg::degree>>; //地理坐标系,单位为度
    	geo_point gp_beijing(116.23128, 40.22077); //北京经纬度坐标
    	geo_point gp_shanghai(121.48941, 31.40527); //上海经纬度坐标
    	auto dis = bg::distance(gp_beijing,gp_shanghai);
    	std::cout << "北京到上海的距离是 " << dis/1000 << " km" << std::endl;
    	return 0;
    }
    

    运行结果

    北京到上海的距离是 1086.91 km
    
  • 点到线段的距离

    #include 
    #include 
    namespace bg = boost::geometry;
    int main(int argc, char *argv[])
    {
    	using geo_point = bg::model::point<
    		double, 2, bg::cs::cartesian>; //笛卡尔坐标系
    	using geo_segment = bg::model::segment<geo_point>; //线段
    	geo_segment seg = { {0,1}, {4,1} };
    	geo_point gp_in(2, 0); //垂足在线段上
    	geo_point gp_out(5, 0); //垂足不在线段上
    	auto dis_in = bg::distance(gp_in,seg);
    	auto dis_out = bg::distance(gp_out, seg);
    	std::cout << "distance from " << bg::dsv(gp_in) << " to " << bg::dsv(seg) << " is " << dis_in << std::endl;
    	std::cout << "distance from " << bg::dsv(gp_out) << " to " << bg::dsv(seg) << " is " << dis_out << std::endl;
    	return 0;
    }
    

    运行结果

    distance from (2, 0) to ((0, 1), (4, 1)) is 1
    distance from (5, 0) to ((0, 1), (4, 1)) is 1.41421
    
  • 点是否在图形内

    #include 
    #include 
    
    namespace bg = boost::geometry;
    int main(int argc, char *argv[])
    {
    	using geo_point = bg::model::point<
    		double, 2, bg::cs::cartesian>; //笛卡尔坐标系
    	using geo_ring = bg::model::ring<geo_point>; //线段
    	geo_ring ring = { {0,0}, {0,2},{2,2},{2,0},{0,0} }; //矩形
    	geo_point gp_in(1, 1); //在图形内
    	geo_point gp_out(3, 0); //不在图形内
    	auto is_in = bg::within(gp_in, ring); //是否在图形内部
    	auto is_out = bg::within(gp_out, ring); //是否在图形内部
    	std::cout << bg::dsv(gp_in) << " locates inside" << bg::dsv(ring) << " " << is_in << std::endl;
    	std::cout  << bg::dsv(gp_out) << " locates inside" << bg::dsv(ring) << " " << is_out << std::endl;
    	return 0;
    }
    

    运行结果

    (1, 1) locates inside((0, 0), (0, 2), (2, 2), (2, 0), (0, 0)) 1
    (3, 0) locates inside((0, 0), (0, 2), (2, 2), (2, 0), (0, 0)) 0
    
  • 点乘操作

    #include 
    #include 
    namespace bg = boost::geometry;
    int main(int argc, char *argv[])
    {
    	using geo_point = bg::model::point<
    		double, 2, bg::cs::cartesian>; //笛卡尔坐标系
    	geo_point gp1(1, 0),gp2(1,1),gp3(0,1);
    	auto dp12 = bg::dot_product(gp1,gp2);
    	auto dp13 = bg::dot_product(gp1, gp3);
    	std::cout << bg::dsv(gp1) << "dot product" << bg::dsv(gp2) << "=" << dp12 << std::endl;
    	std::cout  << bg::dsv(gp1) << "dot product" << bg::dsv(gp3) << "=" << dp13 << std::endl;
    	return 0;
    }
    

    运行结果

    (1, 0)dot product(1, 1)=1
    (1, 0)dot product(0, 1)=0
    

求外接矩形AABB

#include 
#include 
namespace bg = boost::geometry;
int main(int argc, char *argv[])
{
	using geo_point = bg::model::point<
		double, 2, bg::cs::cartesian>; //笛卡尔坐标系
	using geo_line = bg::model::linestring<geo_point>; //线
	using geo_box = bg::model::box<geo_point>; //box
	geo_line line = { {1,1},{2,0},{3,2} };
	geo_box box;
	bg::envelope(line, box);
	std::cout << bg::dsv(line) << "'s AABB is " << bg::dsv(box) << std::endl;
	return 0;
}
((1, 1), (2, 0), (3, 2))'s AABB is ((1, 0), (3, 2))
  • 自定义点,继承?no
    当我们想在点结构中加入其他信息,比如一个字段id,该怎么办?你可能会使用继承,但这样你就不能用相关算法了,因为想要使用相关算法,类必须要满足对应的concept。那如何是好?你应该将自定义类,用BOOST_GEOMETRY_REGISTER*宏,注册到boost的native geometry type中。啥?来个例子你就明白了。

    #include 
    #include 
    #include 
    struct MyPoint //自定义的点
    {
    	MyPoint(double x, double y, int id) :m_x(x), m_y(y), m_id(id) {}
    	double m_x;
    	double m_y;
    	int m_id; //标识id
    };
    //将MyPoint注册到bg的native类型中
    BOOST_GEOMETRY_REGISTER_POINT_2D(MyPoint, double, cs::cartesian, m_x, m_y) 
    namespace bg = boost::geometry;
    int main(int argc, char *argv[])
    {
    	using geo_point = MyPoint;
    	geo_point gp1(0,0,1), gp2(1,0,2);
    	auto dis = bg::distance(gp1,gp2);
    	std::cout << bg::dsv(gp1) << " distant from " << bg::dsv(gp2) << " is " << dis <<std::endl;
    	return 0;
    }
    

    运行结果

    (0, 0) distant from (1, 0) is 1
    

你可能感兴趣的:(c++,地图,图形,c++,图形渲染,几何学)